зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central into mozilla-inbound
This commit is contained in:
Коммит
92ab9f79c9
|
@ -0,0 +1,17 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
|
||||
<script>
|
||||
function boom()
|
||||
{
|
||||
var fr = document.createElementNS("http://www.w3.org/1999/xhtml", "iframe");
|
||||
fr.src = "data:text/html,X";
|
||||
document.documentElement.appendChild(fr);
|
||||
var docType = fr.contentDocument.implementation.createDocumentType(undefined, "", "");
|
||||
document.removeChild(document.documentElement);
|
||||
document.appendChild(docType);
|
||||
}
|
||||
|
||||
window.addEventListener("load", boom, false);
|
||||
</script>
|
||||
|
||||
</svg>
|
После Ширина: | Высота: | Размер: 505 B |
|
@ -80,6 +80,7 @@ load 575462.svg
|
|||
load 590395-1.html
|
||||
load 595606-1.html
|
||||
load 595606-2.html
|
||||
load 605672-1.svg
|
||||
load 606729-1.html
|
||||
load 593302-1.html
|
||||
load 593302-2.html
|
||||
|
|
|
@ -1595,17 +1595,6 @@ public:
|
|||
|
||||
static void StripNullChars(const nsAString& aInStr, nsAString& aOutStr);
|
||||
|
||||
/**
|
||||
* Creates a structured clone of the given jsval according to the algorithm
|
||||
* at:
|
||||
* http://www.whatwg.org/specs/web-apps/current-work/multipage/
|
||||
* urls.html#safe-passing-of-structured-data
|
||||
*
|
||||
* If the function returns a success code then rval is set to point at the
|
||||
* cloned jsval. rval is not set if the function returns a failure code.
|
||||
*/
|
||||
static nsresult CreateStructuredClone(JSContext* cx, jsval val, jsval* rval);
|
||||
|
||||
/**
|
||||
* Strip all \n, \r and nulls from the given string
|
||||
* @param aString the string to remove newlines from [in/out]
|
||||
|
|
|
@ -42,7 +42,8 @@
|
|||
|
||||
/* A namespace class for static layout utilities. */
|
||||
|
||||
#include "jscntxt.h"
|
||||
#include "jsapi.h"
|
||||
#include "jsdbgapi.h"
|
||||
|
||||
#include "nsJSUtils.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
@ -191,11 +192,6 @@ static NS_DEFINE_CID(kXTFServiceCID, NS_XTFSERVICE_CID);
|
|||
|
||||
#include "mozAutoDocUpdate.h"
|
||||
#include "imgICache.h"
|
||||
#include "jsinterp.h"
|
||||
#include "jsarray.h"
|
||||
#include "jsdate.h"
|
||||
#include "jsregexp.h"
|
||||
#include "jstypedarray.h"
|
||||
#include "xpcprivate.h"
|
||||
#include "nsScriptSecurityManager.h"
|
||||
#include "nsIChannelPolicy.h"
|
||||
|
@ -5283,395 +5279,6 @@ nsContentUtils::StripNullChars(const nsAString& aInStr, nsAString& aOutStr)
|
|||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
const unsigned int kCloneStackFrameStackSize = 20;
|
||||
|
||||
class CloneStackFrame
|
||||
{
|
||||
friend class CloneStack;
|
||||
|
||||
public:
|
||||
// These three jsvals must all stick together as they're treated as a jsval
|
||||
// array!
|
||||
jsval source;
|
||||
jsval clone;
|
||||
jsval temp;
|
||||
js::AutoIdArray ids;
|
||||
jsuint index;
|
||||
|
||||
private:
|
||||
// Only let CloneStack access these.
|
||||
CloneStackFrame(JSContext* aCx, jsval aSource, jsval aClone, JSIdArray* aIds)
|
||||
: source(aSource), clone(aClone), temp(JSVAL_NULL), ids(aCx, aIds), index(0),
|
||||
prevFrame(nsnull), tvrVals(aCx, 3, &source)
|
||||
{
|
||||
MOZ_COUNT_CTOR(CloneStackFrame);
|
||||
}
|
||||
|
||||
~CloneStackFrame()
|
||||
{
|
||||
MOZ_COUNT_DTOR(CloneStackFrame);
|
||||
}
|
||||
|
||||
CloneStackFrame* prevFrame;
|
||||
js::AutoArrayRooter tvrVals;
|
||||
};
|
||||
|
||||
class CloneStack
|
||||
{
|
||||
public:
|
||||
CloneStack(JSContext* cx)
|
||||
: mCx(cx), mLastFrame(nsnull) {
|
||||
mObjectSet.Init();
|
||||
}
|
||||
|
||||
~CloneStack() {
|
||||
while (!IsEmpty()) {
|
||||
Pop();
|
||||
}
|
||||
}
|
||||
|
||||
PRBool
|
||||
Push(jsval source, jsval clone, JSIdArray* ids) {
|
||||
NS_ASSERTION(!JSVAL_IS_PRIMITIVE(source) && !JSVAL_IS_PRIMITIVE(clone),
|
||||
"Must be an object!");
|
||||
if (!ids) {
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
CloneStackFrame* newFrame;
|
||||
if (mObjectSet.Count() < kCloneStackFrameStackSize) {
|
||||
// If the object can fit in our stack space then use that.
|
||||
CloneStackFrame* buf = reinterpret_cast<CloneStackFrame*>(mStackFrames);
|
||||
newFrame = new (buf + mObjectSet.Count())
|
||||
CloneStackFrame(mCx, source, clone, ids);
|
||||
}
|
||||
else {
|
||||
// Use the heap.
|
||||
newFrame = new CloneStackFrame(mCx, source, clone, ids);
|
||||
}
|
||||
|
||||
mObjectSet.PutEntry(JSVAL_TO_OBJECT(source));
|
||||
|
||||
newFrame->prevFrame = mLastFrame;
|
||||
mLastFrame = newFrame;
|
||||
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
CloneStackFrame*
|
||||
Peek() {
|
||||
return mLastFrame;
|
||||
}
|
||||
|
||||
void
|
||||
Pop() {
|
||||
if (IsEmpty()) {
|
||||
NS_ERROR("Empty stack!");
|
||||
return;
|
||||
}
|
||||
|
||||
CloneStackFrame* lastFrame = mLastFrame;
|
||||
|
||||
mObjectSet.RemoveEntry(JSVAL_TO_OBJECT(lastFrame->source));
|
||||
mLastFrame = lastFrame->prevFrame;
|
||||
|
||||
if (mObjectSet.Count() >= kCloneStackFrameStackSize) {
|
||||
// Only delete if this was a heap object.
|
||||
delete lastFrame;
|
||||
}
|
||||
else {
|
||||
// Otherwise just run the destructor.
|
||||
lastFrame->~CloneStackFrame();
|
||||
}
|
||||
}
|
||||
|
||||
PRBool
|
||||
IsEmpty() {
|
||||
NS_ASSERTION((!mLastFrame && !mObjectSet.Count()) ||
|
||||
(mLastFrame && mObjectSet.Count()),
|
||||
"Hashset is out of sync!");
|
||||
return mObjectSet.Count() == 0;
|
||||
}
|
||||
|
||||
PRBool
|
||||
Search(JSObject* obj) {
|
||||
return !!mObjectSet.GetEntry(obj);
|
||||
}
|
||||
|
||||
private:
|
||||
JSContext* mCx;
|
||||
CloneStackFrame* mLastFrame;
|
||||
nsTHashtable<nsVoidPtrHashKey> mObjectSet;
|
||||
|
||||
// Use a char array instead of CloneStackFrame array to prevent the JSAuto*
|
||||
// helpers from running until we're ready for them.
|
||||
char mStackFrames[kCloneStackFrameStackSize * sizeof(CloneStackFrame)];
|
||||
};
|
||||
|
||||
struct ReparentObjectData {
|
||||
ReparentObjectData(JSContext* cx, JSObject* obj)
|
||||
: cx(cx), obj(obj), ids(nsnull), index(0) { }
|
||||
|
||||
~ReparentObjectData() {
|
||||
if (ids) {
|
||||
JS_DestroyIdArray(cx, ids);
|
||||
}
|
||||
}
|
||||
|
||||
JSContext* cx;
|
||||
JSObject* obj;
|
||||
JSIdArray* ids;
|
||||
jsint index;
|
||||
};
|
||||
|
||||
inline nsresult
|
||||
SetPropertyOnValueOrObject(JSContext* cx,
|
||||
jsval val,
|
||||
jsval* rval,
|
||||
JSObject* obj,
|
||||
jsid id)
|
||||
{
|
||||
NS_ASSERTION((rval && !obj) || (!rval && obj), "Can only clone to one dest!");
|
||||
if (rval) {
|
||||
*rval = val;
|
||||
return NS_OK;
|
||||
}
|
||||
if (!JS_DefinePropertyById(cx, obj, id, val, nsnull, nsnull,
|
||||
JSPROP_ENUMERATE)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
inline JSObject*
|
||||
CreateEmptyObjectOrArray(JSContext* cx,
|
||||
JSObject* obj)
|
||||
{
|
||||
if (JS_IsArrayObject(cx, obj)) {
|
||||
jsuint length;
|
||||
if (!JS_GetArrayLength(cx, obj, &length)) {
|
||||
NS_ERROR("Failed to get array length?!");
|
||||
return nsnull;
|
||||
}
|
||||
return JS_NewArrayObject(cx, length, NULL);
|
||||
}
|
||||
return JS_NewObject(cx, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
nsresult
|
||||
CloneSimpleValues(JSContext* cx,
|
||||
jsval val,
|
||||
jsval* rval,
|
||||
PRBool* wasCloned,
|
||||
JSObject* robj = nsnull,
|
||||
jsid rid = INT_TO_JSID(0))
|
||||
{
|
||||
*wasCloned = PR_TRUE;
|
||||
|
||||
// No cloning necessary for these non-GC'd jsvals.
|
||||
if (!JSVAL_IS_GCTHING(val) || JSVAL_IS_NULL(val)) {
|
||||
return SetPropertyOnValueOrObject(cx, val, rval, robj, rid);
|
||||
}
|
||||
|
||||
// We'll use immutable strings to prevent copying if we can.
|
||||
if (JSVAL_IS_STRING(val)) {
|
||||
if (!JS_MakeStringImmutable(cx, JSVAL_TO_STRING(val))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
return SetPropertyOnValueOrObject(cx, val, rval, robj, rid);
|
||||
}
|
||||
|
||||
NS_ASSERTION(!JSVAL_IS_PRIMITIVE(val), "Not an object!");
|
||||
JSObject* obj = JSVAL_TO_OBJECT(val);
|
||||
|
||||
// Dense arrays of primitives can be cloned quickly.
|
||||
JSObject* newArray;
|
||||
if (!js_CloneDensePrimitiveArray(cx, obj, &newArray)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
if (newArray) {
|
||||
return SetPropertyOnValueOrObject(cx, OBJECT_TO_JSVAL(newArray), rval, robj,
|
||||
rid);
|
||||
}
|
||||
|
||||
// Date objects.
|
||||
if (js_DateIsValid(cx, obj)) {
|
||||
jsdouble msec = js_DateGetMsecSinceEpoch(cx, obj);
|
||||
JSObject* newDate;
|
||||
if (!(msec && (newDate = js_NewDateObjectMsec(cx, msec)))) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
return SetPropertyOnValueOrObject(cx, OBJECT_TO_JSVAL(newDate), rval, robj,
|
||||
rid);
|
||||
}
|
||||
|
||||
// RegExp objects.
|
||||
if (js_ObjectIsRegExp(obj)) {
|
||||
JSObject* proto;
|
||||
if (!js_GetClassPrototype(cx, JS_GetScopeChain(cx), JSProto_RegExp,
|
||||
&proto)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
JSObject* newRegExp = js_CloneRegExpObject(cx, obj, proto);
|
||||
if (!newRegExp) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
return SetPropertyOnValueOrObject(cx, OBJECT_TO_JSVAL(newRegExp), rval,
|
||||
robj, rid);
|
||||
}
|
||||
|
||||
// Typed array objects.
|
||||
if (js_IsTypedArray(obj)) {
|
||||
js::TypedArray* src = js::TypedArray::fromJSObject(obj);
|
||||
JSObject* newTypedArray = js_CreateTypedArrayWithArray(cx, src->type, obj);
|
||||
if (!newTypedArray) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
return SetPropertyOnValueOrObject(cx, OBJECT_TO_JSVAL(newTypedArray), rval,
|
||||
robj, rid);
|
||||
}
|
||||
|
||||
// ArrayBuffer objects.
|
||||
if (js_IsArrayBuffer(obj)) {
|
||||
js::ArrayBuffer* src = js::ArrayBuffer::fromJSObject(obj);
|
||||
if (!src) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
JSObject* newBuffer = js_CreateArrayBuffer(cx, src->byteLength);
|
||||
if (!newBuffer) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
memcpy(js::ArrayBuffer::fromJSObject(newBuffer)->data, src->data,
|
||||
src->byteLength);
|
||||
return SetPropertyOnValueOrObject(cx, OBJECT_TO_JSVAL(newBuffer), rval,
|
||||
robj, rid);
|
||||
}
|
||||
|
||||
// Do we support File?
|
||||
// Do we support Blob?
|
||||
// Do we support FileList?
|
||||
|
||||
// Function objects don't get cloned.
|
||||
if (JS_ObjectIsFunction(cx, obj)) {
|
||||
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
|
||||
}
|
||||
|
||||
// Security wrapped objects are not allowed either.
|
||||
if (obj->isWrapper() && !obj->getClass()->ext.innerObject)
|
||||
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
|
||||
|
||||
// See if this JSObject is backed by some C++ object. If it is then we assume
|
||||
// that it is inappropriate to clone.
|
||||
nsCOMPtr<nsIXPConnectWrappedNative> wrapper;
|
||||
nsContentUtils::XPConnect()->
|
||||
GetWrappedNativeOfJSObject(cx, obj, getter_AddRefs(wrapper));
|
||||
if (wrapper) {
|
||||
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
|
||||
}
|
||||
|
||||
*wasCloned = PR_FALSE;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
// static
|
||||
nsresult
|
||||
nsContentUtils::CreateStructuredClone(JSContext* cx,
|
||||
jsval val,
|
||||
jsval* rval)
|
||||
{
|
||||
JSAutoRequest ar(cx);
|
||||
|
||||
nsCOMPtr<nsIXPConnect> xpconnect(sXPConnect);
|
||||
NS_ENSURE_STATE(xpconnect);
|
||||
|
||||
PRBool wasCloned;
|
||||
nsresult rv = CloneSimpleValues(cx, val, rval, &wasCloned);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (wasCloned) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_ASSERTION(JSVAL_IS_OBJECT(val), "Not an object?!");
|
||||
JSObject* obj = CreateEmptyObjectOrArray(cx, JSVAL_TO_OBJECT(val));
|
||||
if (!obj) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
jsval output = OBJECT_TO_JSVAL(obj);
|
||||
js::AutoValueRooter tvr(cx, output);
|
||||
|
||||
CloneStack stack(cx);
|
||||
if (!stack.Push(val, OBJECT_TO_JSVAL(obj),
|
||||
JS_Enumerate(cx, JSVAL_TO_OBJECT(val)))) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
while (!stack.IsEmpty()) {
|
||||
CloneStackFrame* frame = stack.Peek();
|
||||
|
||||
NS_ASSERTION(!!frame->ids &&
|
||||
frame->ids.length() >= frame->index &&
|
||||
!JSVAL_IS_PRIMITIVE(frame->source) &&
|
||||
!JSVAL_IS_PRIMITIVE(frame->clone),
|
||||
"Bad frame state!");
|
||||
|
||||
if (frame->index == frame->ids.length()) {
|
||||
// Done cloning this object, pop the frame.
|
||||
stack.Pop();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get the current id and increment the index.
|
||||
jsid id = frame->ids[frame->index++];
|
||||
|
||||
if (!JS_GetPropertyById(cx, JSVAL_TO_OBJECT(frame->source), id,
|
||||
&frame->temp)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (!JSVAL_IS_PRIMITIVE(frame->temp) &&
|
||||
stack.Search(JSVAL_TO_OBJECT(frame->temp))) {
|
||||
// Spec says to throw this particular exception for cyclical references.
|
||||
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
|
||||
}
|
||||
|
||||
JSObject* clone = JSVAL_TO_OBJECT(frame->clone);
|
||||
|
||||
PRBool wasCloned;
|
||||
nsresult rv = CloneSimpleValues(cx, frame->temp, nsnull, &wasCloned, clone,
|
||||
id);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (!wasCloned) {
|
||||
NS_ASSERTION(JSVAL_IS_OBJECT(frame->temp), "Not an object?!");
|
||||
obj = CreateEmptyObjectOrArray(cx, JSVAL_TO_OBJECT(frame->temp));
|
||||
if (!obj ||
|
||||
!stack.Push(frame->temp, OBJECT_TO_JSVAL(obj),
|
||||
JS_Enumerate(cx, JSVAL_TO_OBJECT(frame->temp)))) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
// Set the new object as a property of the clone. We'll fill it on the
|
||||
// next iteration.
|
||||
if (!JS_DefinePropertyById(cx, clone, id, OBJECT_TO_JSVAL(obj), nsnull,
|
||||
nsnull, JSPROP_ENUMERATE)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*rval = output;
|
||||
return NS_OK;
|
||||
}
|
||||
struct ClassMatchingInfo {
|
||||
nsAttrValue::AtomArray mClasses;
|
||||
nsCaseTreatment mCaseTreatment;
|
||||
|
|
|
@ -234,7 +234,7 @@ protected:
|
|||
nsresult AppendVoidPtr(void* aData, PRUint32 aLength);
|
||||
nsresult AppendString(JSString* aString, JSContext* aCx);
|
||||
nsresult AppendBlob(nsIDOMBlob* aBlob);
|
||||
nsresult AppendArrayBuffer(js::ArrayBuffer* aBuffer);
|
||||
nsresult AppendArrayBuffer(JSObject* aBuffer);
|
||||
|
||||
bool ExpandBufferSize(PRUint64 aSize)
|
||||
{
|
||||
|
@ -332,9 +332,9 @@ nsDOMBlobBuilder::AppendBlob(nsIDOMBlob* aBlob)
|
|||
}
|
||||
|
||||
nsresult
|
||||
nsDOMBlobBuilder::AppendArrayBuffer(js::ArrayBuffer* aBuffer)
|
||||
nsDOMBlobBuilder::AppendArrayBuffer(JSObject* aBuffer)
|
||||
{
|
||||
return AppendVoidPtr(aBuffer->data, aBuffer->byteLength);
|
||||
return AppendVoidPtr(JS_GetArrayBufferData(aBuffer), JS_GetArrayBufferByteLength(aBuffer));
|
||||
}
|
||||
|
||||
/* nsIDOMBlob getBlob ([optional] in DOMString contentType); */
|
||||
|
@ -379,7 +379,7 @@ nsDOMBlobBuilder::Append(const jsval& aData, JSContext* aCx)
|
|||
|
||||
// Is it an array buffer?
|
||||
if (js_IsArrayBuffer(obj)) {
|
||||
js::ArrayBuffer* buffer = js::ArrayBuffer::fromJSObject(obj);
|
||||
JSObject* buffer = js::ArrayBuffer::getArrayBuffer(obj);
|
||||
if (buffer)
|
||||
return AppendArrayBuffer(buffer);
|
||||
}
|
||||
|
|
|
@ -42,7 +42,6 @@
|
|||
#include "nsContentUtils.h"
|
||||
#include "nsIXPConnect.h"
|
||||
#include "jsapi.h"
|
||||
#include "jsarray.h"
|
||||
#include "jsinterp.h"
|
||||
#include "nsJSUtils.h"
|
||||
#include "nsNetUtil.h"
|
||||
|
|
|
@ -864,9 +864,9 @@ nsresult nsXMLHttpRequest::CreateResponseArrayBuffer(JSContext *aCx)
|
|||
}
|
||||
|
||||
if (dataLen > 0) {
|
||||
js::ArrayBuffer *abuf = js::ArrayBuffer::fromJSObject(mResultArrayBuffer);
|
||||
JSObject *abuf = js::ArrayBuffer::getArrayBuffer(mResultArrayBuffer);
|
||||
NS_ASSERTION(abuf, "What happened?");
|
||||
memcpy(abuf->data, mResponseBody.BeginReading(), dataLen);
|
||||
memcpy(JS_GetArrayBufferData(abuf), mResponseBody.BeginReading(), dataLen);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
|
|
@ -102,7 +102,7 @@ nsIDOMWebGLRenderingContext_BufferData(JSContext *cx, uintN argc, jsval *vp)
|
|||
|
||||
int32 target;
|
||||
js::TypedArray *wa = 0;
|
||||
js::ArrayBuffer *wb = 0;
|
||||
JSObject *wb = 0;
|
||||
int32 size;
|
||||
int32 usage;
|
||||
|
||||
|
@ -118,7 +118,7 @@ nsIDOMWebGLRenderingContext_BufferData(JSContext *cx, uintN argc, jsval *vp)
|
|||
|
||||
JSObject *arg2 = JSVAL_TO_OBJECT(argv[1]);
|
||||
if (js_IsArrayBuffer(arg2)) {
|
||||
wb = js::ArrayBuffer::fromJSObject(arg2);
|
||||
wb = js::ArrayBuffer::getArrayBuffer(arg2);
|
||||
} else if (js_IsTypedArray(arg2)) {
|
||||
wa = js::TypedArray::fromJSObject(arg2);
|
||||
}
|
||||
|
@ -176,7 +176,7 @@ nsIDOMWebGLRenderingContext_BufferSubData(JSContext *cx, uintN argc, jsval *vp)
|
|||
int32 target;
|
||||
int32 offset;
|
||||
js::TypedArray *wa = 0;
|
||||
js::ArrayBuffer *wb = 0;
|
||||
JSObject *wb = 0;
|
||||
|
||||
if (!JS_ValueToECMAInt32(cx, argv[0], &target))
|
||||
return JS_FALSE;
|
||||
|
@ -193,7 +193,7 @@ nsIDOMWebGLRenderingContext_BufferSubData(JSContext *cx, uintN argc, jsval *vp)
|
|||
if (!nullobject) {
|
||||
JSObject *arg3 = JSVAL_TO_OBJECT(argv[2]);
|
||||
if (js_IsArrayBuffer(arg3)) {
|
||||
wb = js::ArrayBuffer::fromJSObject(arg3);
|
||||
wb = js::ArrayBuffer::getArrayBuffer(arg3);
|
||||
} else if (js_IsTypedArray(arg3)) {
|
||||
wa = js::TypedArray::fromJSObject(arg3);
|
||||
} else {
|
||||
|
@ -261,7 +261,7 @@ nsIDOMWebGLRenderingContext_ReadPixels(JSContext *cx, uintN argc, jsval *vp)
|
|||
JSObject *argv6 = JSVAL_TO_OBJECT(argv[6]);
|
||||
if (js_IsArrayBuffer(argv6)) {
|
||||
rv = self->ReadPixels_buf(argv0, argv1, argv2, argv3,
|
||||
argv4, argv5, js::ArrayBuffer::fromJSObject(argv6));
|
||||
argv4, argv5, js::ArrayBuffer::getArrayBuffer(argv6));
|
||||
} else if (js_IsTypedArray(argv6)) {
|
||||
rv = self->ReadPixels_array(argv0, argv1, argv2, argv3,
|
||||
argv4, argv5,
|
||||
|
@ -381,7 +381,7 @@ nsIDOMWebGLRenderingContext_TexImage2D(JSContext *cx, uintN argc, jsval *vp)
|
|||
} else if (js_IsArrayBuffer(argv8)) {
|
||||
rv = self->TexImage2D_buf(argv0, argv1, argv2, argv3,
|
||||
argv4, argv5, argv6, argv7,
|
||||
js::ArrayBuffer::fromJSObject(argv8));
|
||||
js::ArrayBuffer::getArrayBuffer(argv8));
|
||||
} else if (js_IsTypedArray(argv8)) {
|
||||
rv = self->TexImage2D_array(argv0, argv1, argv2, argv3,
|
||||
argv4, argv5, argv6, argv7,
|
||||
|
@ -493,7 +493,7 @@ nsIDOMWebGLRenderingContext_TexSubImage2D(JSContext *cx, uintN argc, jsval *vp)
|
|||
if (js_IsArrayBuffer(argv8)) {
|
||||
rv = self->TexSubImage2D_buf(argv0, argv1, argv2, argv3,
|
||||
argv4, argv5, argv6, argv7,
|
||||
js::ArrayBuffer::fromJSObject(argv8));
|
||||
js::ArrayBuffer::getArrayBuffer(argv8));
|
||||
} else if (js_IsTypedArray(argv8)) {
|
||||
rv = self->TexSubImage2D_array(argv0, argv1, argv2, argv3,
|
||||
argv4, argv5, argv6, argv7,
|
||||
|
|
|
@ -418,7 +418,7 @@ WebGLContext::BufferData_size(WebGLenum target, WebGLsizei size, WebGLenum usage
|
|||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WebGLContext::BufferData_buf(WebGLenum target, js::ArrayBuffer *wb, WebGLenum usage)
|
||||
WebGLContext::BufferData_buf(WebGLenum target, JSObject *wb, WebGLenum usage)
|
||||
{
|
||||
WebGLBuffer *boundBuffer = NULL;
|
||||
|
||||
|
@ -438,12 +438,12 @@ WebGLContext::BufferData_buf(WebGLenum target, js::ArrayBuffer *wb, WebGLenum us
|
|||
|
||||
MakeContextCurrent();
|
||||
|
||||
boundBuffer->SetByteLength(wb->byteLength);
|
||||
if (!boundBuffer->CopyDataIfElementArray(wb->data))
|
||||
boundBuffer->SetByteLength(JS_GetArrayBufferByteLength(wb));
|
||||
if (!boundBuffer->CopyDataIfElementArray(JS_GetArrayBufferData(wb)))
|
||||
return ErrorOutOfMemory("bufferData: out of memory");
|
||||
boundBuffer->InvalidateCachedMaxElements();
|
||||
|
||||
gl->fBufferData(target, wb->byteLength, wb->data, usage);
|
||||
gl->fBufferData(target, JS_GetArrayBufferByteLength(wb), JS_GetArrayBufferData(wb), usage);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -492,7 +492,7 @@ WebGLContext::BufferSubData_null()
|
|||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WebGLContext::BufferSubData_buf(GLenum target, WebGLsizei byteOffset, js::ArrayBuffer *wb)
|
||||
WebGLContext::BufferSubData_buf(GLenum target, WebGLsizei byteOffset, JSObject *wb)
|
||||
{
|
||||
WebGLBuffer *boundBuffer = NULL;
|
||||
|
||||
|
@ -510,20 +510,20 @@ WebGLContext::BufferSubData_buf(GLenum target, WebGLsizei byteOffset, js::ArrayB
|
|||
if (!boundBuffer)
|
||||
return ErrorInvalidOperation("BufferData: no buffer bound!");
|
||||
|
||||
CheckedUint32 checked_neededByteLength = CheckedUint32(byteOffset) + wb->byteLength;
|
||||
CheckedUint32 checked_neededByteLength = CheckedUint32(byteOffset) + JS_GetArrayBufferByteLength(wb);
|
||||
if (!checked_neededByteLength.valid())
|
||||
return ErrorInvalidOperation("bufferSubData: integer overflow computing the needed byte length");
|
||||
|
||||
if (checked_neededByteLength.value() > boundBuffer->ByteLength())
|
||||
return ErrorInvalidOperation("BufferSubData: not enough data - operation requires %d bytes, but buffer only has %d bytes",
|
||||
byteOffset, wb->byteLength, boundBuffer->ByteLength());
|
||||
byteOffset, JS_GetArrayBufferByteLength(wb), boundBuffer->ByteLength());
|
||||
|
||||
MakeContextCurrent();
|
||||
|
||||
boundBuffer->CopySubDataIfElementArray(byteOffset, wb->byteLength, wb->data);
|
||||
boundBuffer->CopySubDataIfElementArray(byteOffset, JS_GetArrayBufferByteLength(wb), JS_GetArrayBufferData(wb));
|
||||
boundBuffer->InvalidateCachedMaxElements();
|
||||
|
||||
gl->fBufferSubData(target, byteOffset, wb->byteLength, wb->data);
|
||||
gl->fBufferSubData(target, byteOffset, JS_GetArrayBufferByteLength(wb), JS_GetArrayBufferData(wb));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -3084,11 +3084,11 @@ WebGLContext::ReadPixels_array(WebGLint x, WebGLint y, WebGLsizei width, WebGLsi
|
|||
|
||||
NS_IMETHODIMP
|
||||
WebGLContext::ReadPixels_buf(WebGLint x, WebGLint y, WebGLsizei width, WebGLsizei height,
|
||||
WebGLenum format, WebGLenum type, js::ArrayBuffer *pixels)
|
||||
WebGLenum format, WebGLenum type, JSObject *pixels)
|
||||
{
|
||||
return ReadPixels_base(x, y, width, height, format, type,
|
||||
pixels ? pixels->data : 0,
|
||||
pixels ? pixels->byteLength : 0);
|
||||
pixels ? JS_GetArrayBufferData(pixels) : 0,
|
||||
pixels ? JS_GetArrayBufferByteLength(pixels) : 0);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -4283,11 +4283,11 @@ NS_IMETHODIMP
|
|||
WebGLContext::TexImage2D_buf(WebGLenum target, WebGLint level, WebGLenum internalformat,
|
||||
WebGLsizei width, WebGLsizei height, WebGLint border,
|
||||
WebGLenum format, WebGLenum type,
|
||||
js::ArrayBuffer *pixels)
|
||||
JSObject *pixels)
|
||||
{
|
||||
return TexImage2D_base(target, level, internalformat, width, height, 0, border, format, type,
|
||||
pixels ? pixels->data : 0,
|
||||
pixels ? pixels->byteLength : 0,
|
||||
pixels ? JS_GetArrayBufferData(pixels) : 0,
|
||||
pixels ? JS_GetArrayBufferByteLength(pixels) : 0,
|
||||
-1,
|
||||
WebGLTexelFormat::Auto, PR_FALSE);
|
||||
}
|
||||
|
@ -4458,14 +4458,14 @@ WebGLContext::TexSubImage2D_buf(WebGLenum target, WebGLint level,
|
|||
WebGLint xoffset, WebGLint yoffset,
|
||||
WebGLsizei width, WebGLsizei height,
|
||||
WebGLenum format, WebGLenum type,
|
||||
js::ArrayBuffer *pixels)
|
||||
JSObject *pixels)
|
||||
{
|
||||
if (!pixels)
|
||||
return ErrorInvalidValue("TexSubImage2D: pixels must not be null!");
|
||||
|
||||
return TexSubImage2D_base(target, level, xoffset, yoffset,
|
||||
width, height, 0, format, type,
|
||||
pixels->data, pixels->byteLength,
|
||||
JS_GetArrayBufferData(pixels), JS_GetArrayBufferByteLength(pixels),
|
||||
-1,
|
||||
WebGLTexelFormat::Auto, PR_FALSE);
|
||||
}
|
||||
|
|
|
@ -859,21 +859,6 @@ nsHTMLInputElement::AfterSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
|
|||
|
||||
UpdateBarredFromConstraintValidation();
|
||||
|
||||
// If we are changing type from File/Text/Tel/Passwd to other input types
|
||||
// we need save the mValue into value attribute
|
||||
if (mInputData.mValue &&
|
||||
mType != NS_FORM_INPUT_EMAIL &&
|
||||
mType != NS_FORM_INPUT_TEXT &&
|
||||
mType != NS_FORM_INPUT_SEARCH &&
|
||||
mType != NS_FORM_INPUT_PASSWORD &&
|
||||
mType != NS_FORM_INPUT_TEL &&
|
||||
mType != NS_FORM_INPUT_URL &&
|
||||
mType != NS_FORM_INPUT_FILE) {
|
||||
SetAttr(kNameSpaceID_None, nsGkAtoms::value,
|
||||
NS_ConvertUTF8toUTF16(mInputData.mValue), PR_FALSE);
|
||||
FreeData();
|
||||
}
|
||||
|
||||
if (mType != NS_FORM_INPUT_IMAGE) {
|
||||
// We're no longer an image input. Cancel our image requests, if we have
|
||||
// any. Note that doing this when we already weren't an image is ok --
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<script>
|
||||
|
||||
function boom()
|
||||
{
|
||||
var f = document.getElementById("f");
|
||||
|
||||
for (var i = 0; i < 50; ++i) {
|
||||
f.contentWindow.history.pushState({}, "");
|
||||
}
|
||||
|
||||
document.body.removeChild(f);
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body onload="boom();"><iframe id="f" src="data:text/html,1"></iframe></body>
|
||||
</html>
|
|
@ -9,3 +9,4 @@ load 436900-1.html
|
|||
asserts(0-2) load 436900-2.html # bug 566159
|
||||
load 500328-1.html
|
||||
load 514779-1.xhtml
|
||||
load 614499-1.html
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
<html xmlns="http://www.w3.org/1999/xhtml" class="reftest-wait">
|
||||
<head>
|
||||
<script>
|
||||
<![CDATA[
|
||||
|
||||
function x()
|
||||
{
|
||||
var p = document.createElementNS("http://www.w3.org/1999/xhtml", "iframe");
|
||||
var frame = document.createElementNS("http://www.w3.org/1999/xhtml", "iframe");
|
||||
frame.onload = y;
|
||||
frame.src = "data:text/html,1";
|
||||
document.body.appendChild(frame);
|
||||
var frameRoot = frame.contentDocument.documentElement;
|
||||
|
||||
function y()
|
||||
{
|
||||
var frameDoc = frameRoot.ownerDocument;
|
||||
frameRoot.appendChild(p);
|
||||
var attr = frameDoc.createAttributeNS("http://www.w3.org/1999/xhtml", "u");
|
||||
attr.w = {};
|
||||
p.setAttributeNode(attr);
|
||||
document.documentElement.removeAttribute("class");
|
||||
}
|
||||
}
|
||||
|
||||
]]>
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body onload="setTimeout(x, 0);"></body>
|
||||
</html>
|
|
@ -24,5 +24,6 @@ load 502617.html
|
|||
asserts(1) load 504224.html # bug 564098
|
||||
load 603531.html
|
||||
load 601247.html
|
||||
load 609560-1.xhtml
|
||||
load 612018-1.html
|
||||
load 637116.html
|
||||
|
|
|
@ -68,8 +68,8 @@ struct TypedArray;
|
|||
#undef NO_ERROR
|
||||
%}
|
||||
|
||||
[ptr] native WebGLArrayBufferPtr (js::ArrayBuffer);
|
||||
[ptr] native WebGLArrayPtr (js::TypedArray);
|
||||
[ptr] native WebGLJSObjectPtr (JSObject);
|
||||
|
||||
//
|
||||
// OpenGL object wrappers
|
||||
|
@ -140,7 +140,7 @@ interface nsIWebGLExtension : nsISupports
|
|||
{
|
||||
};
|
||||
|
||||
[scriptable, uuid(44119fc2-b5f0-4acc-b836-5d28cc43e8fb)]
|
||||
[scriptable, uuid(ef15ae85-4670-4dc4-848d-51ca81e8397a)]
|
||||
interface nsIDOMWebGLRenderingContext : nsISupports
|
||||
{
|
||||
//
|
||||
|
@ -587,12 +587,12 @@ interface nsIDOMWebGLRenderingContext : nsISupports
|
|||
// Modified: void glBufferData(WebGLenum target, long size, const void* data, WebGLenum usage);
|
||||
void bufferData([optional] in long dummy);
|
||||
[noscript] void bufferData_size(in WebGLenum target, in WebGLsizei size, in WebGLenum usage);
|
||||
[noscript] void bufferData_buf(in WebGLenum target, in WebGLArrayBufferPtr data, in WebGLenum usage);
|
||||
[noscript] void bufferData_buf(in WebGLenum target, in WebGLJSObjectPtr data, in WebGLenum usage);
|
||||
[noscript] void bufferData_array(in WebGLenum target, in WebGLArrayPtr data, in WebGLenum usage);
|
||||
[noscript] void bufferData_null();
|
||||
|
||||
void bufferSubData([optional] in long dummy);
|
||||
[noscript] void bufferSubData_buf(in WebGLenum target, in long offset, in WebGLArrayBufferPtr data);
|
||||
[noscript] void bufferSubData_buf(in WebGLenum target, in long offset, in WebGLJSObjectPtr data);
|
||||
[noscript] void bufferSubData_array(in WebGLenum target, in long offset, in WebGLArrayPtr data);
|
||||
[noscript] void bufferSubData_null();
|
||||
|
||||
|
@ -716,7 +716,7 @@ interface nsIDOMWebGLRenderingContext : nsISupports
|
|||
[noscript] void readPixels_array(in WebGLint x, in WebGLint y, in WebGLsizei width, in WebGLsizei height,
|
||||
in WebGLenum format, in WebGLenum type, in WebGLArrayPtr pixels);
|
||||
[noscript] void readPixels_buf(in WebGLint x, in WebGLint y, in WebGLsizei width, in WebGLsizei height,
|
||||
in WebGLenum format, in WebGLenum type, in WebGLArrayBufferPtr pixels);
|
||||
in WebGLenum format, in WebGLenum type, in WebGLJSObjectPtr pixels);
|
||||
|
||||
//void glReleaseShaderCompiler();
|
||||
|
||||
|
@ -737,7 +737,7 @@ interface nsIDOMWebGLRenderingContext : nsISupports
|
|||
void texImage2D([optional] in long dummy);
|
||||
[noscript] void texImage2D_buf(in WebGLenum target, in WebGLint level, in WebGLenum internalformat,
|
||||
in WebGLsizei width, in WebGLsizei height,
|
||||
in WebGLint border, in WebGLenum format, in WebGLenum type, in WebGLArrayBufferPtr pixels);
|
||||
in WebGLint border, in WebGLenum format, in WebGLenum type, in WebGLJSObjectPtr pixels);
|
||||
[noscript] void texImage2D_array(in WebGLenum target, in WebGLint level, in WebGLenum internalformat,
|
||||
in WebGLsizei width, in WebGLsizei height,
|
||||
in WebGLint border, in WebGLenum format, in WebGLenum type, in WebGLArrayPtr pixels);
|
||||
|
@ -752,7 +752,7 @@ interface nsIDOMWebGLRenderingContext : nsISupports
|
|||
void texSubImage2D([optional] in long dummy);
|
||||
[noscript] void texSubImage2D_buf(in WebGLenum target, in WebGLint level,
|
||||
in WebGLint xoffset, in WebGLint yoffset, in WebGLsizei width, in WebGLsizei height,
|
||||
in WebGLenum format, in WebGLenum type, in WebGLArrayBufferPtr pixels);
|
||||
in WebGLenum format, in WebGLenum type, in WebGLJSObjectPtr pixels);
|
||||
[noscript] void texSubImage2D_array(in WebGLenum target, in WebGLint level,
|
||||
in WebGLint xoffset, in WebGLint yoffset, in WebGLsizei width, in WebGLsizei height,
|
||||
in WebGLenum format, in WebGLenum type, in WebGLArrayPtr pixels);
|
||||
|
|
|
@ -59,7 +59,6 @@
|
|||
#include "nsIPrivateDOMEvent.h"
|
||||
#include "nsFrameLoader.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "jsarray.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsContentPermissionHelper.h"
|
||||
#include "nsIDOMNSHTMLFrameElement.h"
|
||||
|
|
|
@ -39,14 +39,7 @@
|
|||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "jsdtoa.h"
|
||||
#include "jsprvtd.h"
|
||||
#include "jsbool.h"
|
||||
#include "jsarena.h"
|
||||
#include "jscntxt.h"
|
||||
#include "jsinterp.h"
|
||||
#include "jsiter.h"
|
||||
#include "jstypes.h"
|
||||
#include "jsdbgapi.h"
|
||||
#include "nsIServiceManager.h"
|
||||
#include "nsJSON.h"
|
||||
#include "nsIXPConnect.h"
|
||||
|
|
|
@ -78,22 +78,18 @@ var messages = [
|
|||
{
|
||||
type: "object",
|
||||
value: cyclicalObject,
|
||||
exception: true
|
||||
},
|
||||
{
|
||||
type: "object",
|
||||
value: [null, 2, false, cyclicalObject],
|
||||
exception: true
|
||||
},
|
||||
{
|
||||
type: "object",
|
||||
value: cyclicalArray,
|
||||
exception: true
|
||||
},
|
||||
{
|
||||
type: "object",
|
||||
value: {foo: 1, bar: cyclicalArray},
|
||||
exception: true
|
||||
},
|
||||
{
|
||||
type: "object",
|
||||
|
@ -103,7 +99,6 @@ var messages = [
|
|||
{
|
||||
type: "object",
|
||||
value: crazyCyclicalObject,
|
||||
exception: true
|
||||
},
|
||||
{
|
||||
type: "object",
|
||||
|
|
|
@ -4,35 +4,96 @@
|
|||
<title>postMessage structured clone page</title>
|
||||
<script type="application/javascript;version=1.7"
|
||||
src="postMessage_structured_clone_helper.js"></script>
|
||||
<script type="application/javascript">
|
||||
<script type="application/javascript;version=1.7">
|
||||
var generator = new getTestContent()
|
||||
|
||||
function eq(lhs, rhs)
|
||||
{
|
||||
for(p in lhs)
|
||||
{
|
||||
switch(typeof(lhs[p]))
|
||||
{
|
||||
case 'object':
|
||||
if (!eq(lhs[p], rhs[p])) { return false }; break;
|
||||
case 'function':
|
||||
if (typeof(rhs[p])=='undefined' || (p != 'equals' && lhs[p].toString() != rhs[p].toString())) { return false; }; break;
|
||||
default:
|
||||
if (lhs[p] != rhs[p]) { return false; }
|
||||
function isClone(a, b) {
|
||||
window.dump("Object a: " + a + "\n");
|
||||
window.dump("Object b: " + b + "\n");
|
||||
var stack = [[a, b]];
|
||||
var memory = new WeakMap();
|
||||
var rmemory = new WeakMap();
|
||||
|
||||
while (stack.length > 0) {
|
||||
var pair = stack.pop();
|
||||
var x = pair[0], y = pair[1];
|
||||
if (typeof x !== "object" || x === null) {
|
||||
// x is primitive.
|
||||
if (x !== y) {
|
||||
window.dump("Primitives not equal!\n");
|
||||
return false;
|
||||
}
|
||||
} else if (x instanceof Date) {
|
||||
if (x.getTime() == y.getTime())
|
||||
return true;
|
||||
window.dump("Dates not equal!\n");
|
||||
return false;
|
||||
} else if (memory.has(x)) {
|
||||
// x is an object we have seen before in a.
|
||||
if (y !== memory.get(x)) {
|
||||
window.dump("Already seen!?\n");
|
||||
return false;
|
||||
}
|
||||
if (!(rmemory.get(y) == x)) {
|
||||
window.dump("Not equal??\n");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// x is an object we have not seen before.
|
||||
// Check that we have not seen y before either.
|
||||
if (rmemory.has(y)) {
|
||||
// If we have seen y before, the only possible outcome
|
||||
// is that x and y are literally the same object.
|
||||
if (y == x)
|
||||
continue;
|
||||
window.dump("Already seen y!?\n");
|
||||
window.dump(y.toString() + "\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// x and y must be of the same [[Class]].
|
||||
var xcls = Object.prototype.toString.call(x);
|
||||
var ycls = Object.prototype.toString.call(y);
|
||||
if (xcls !== ycls) {
|
||||
window.dump("Failing on proto\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// This function is only designed to check Objects and Arrays.
|
||||
if (!(xcls === "[object Object]" || xcls === "[object Array]")) {
|
||||
window.dump("Not an object!\n");
|
||||
window.dump(xcls + "\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Compare objects.
|
||||
var xk = Object.keys(x), yk = Object.keys(y);
|
||||
if (xk.length !== yk.length) {
|
||||
window.dump("Length mismatch!\n");
|
||||
return false;
|
||||
}
|
||||
for (var i = 0; i < xk.length; i++) {
|
||||
// We must see the same property names in the same order.
|
||||
if (xk[i] !== yk[i]) {
|
||||
window.dump("wrong order\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Put the property values on the stack to compare later.
|
||||
stack.push([x[xk[i]], y[yk[i]]]);
|
||||
}
|
||||
|
||||
// Record that we have seen this pair of objects.
|
||||
memory.set(x, y);
|
||||
rmemory.set(y, x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(p in rhs)
|
||||
{
|
||||
if(typeof(lhs[p])=='undefined') {return false;}
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
function receiveMessage(evt)
|
||||
{
|
||||
if (eq(evt.data,generator.next()))
|
||||
if (isClone(evt.data, generator.next()))
|
||||
window.parent.postMessage("TEST-PASS", "*");
|
||||
else
|
||||
window.parent.postMessage("TEST-FAIL", "*");
|
||||
|
|
|
@ -12,5 +12,52 @@ function getTestContent()
|
|||
obj.foo = 3;
|
||||
obj.bar = "hi";
|
||||
obj.baz = new Date(1306113544);
|
||||
obj.boo = obj;
|
||||
yield obj;
|
||||
|
||||
let recursiveobj = new Object();
|
||||
recursiveobj.a = recursiveobj;
|
||||
recursiveobj.foo = new Object();
|
||||
recursiveobj.foo.bar = "bar";
|
||||
recursiveobj.foo.backref = recursiveobj;
|
||||
recursiveobj.foo.baz = 84;
|
||||
recursiveobj.foo.backref2 = recursiveobj;
|
||||
recursiveobj.bar = new Object();
|
||||
recursiveobj.bar.foo = "foo";
|
||||
recursiveobj.bar.backref = recursiveobj;
|
||||
recursiveobj.bar.baz = new Date(1306113544);
|
||||
recursiveobj.bar.backref2 = recursiveobj;
|
||||
recursiveobj.expando = recursiveobj;
|
||||
yield recursiveobj;
|
||||
|
||||
let obj = new Object();
|
||||
obj.expando1 = 1;
|
||||
obj.foo = new Object();
|
||||
obj.foo.bar = 2;
|
||||
obj.bar = new Object();
|
||||
obj.bar.foo = obj.foo;
|
||||
obj.expando = new Object();
|
||||
obj.expando.expando = new Object();
|
||||
obj.expando.expando.obj = obj;
|
||||
obj.expando2 = 4;
|
||||
obj.baz = obj.expando.expando;
|
||||
obj.blah = obj.bar;
|
||||
obj.foo.baz = obj.blah;
|
||||
obj.foo.blah = obj.blah;
|
||||
yield obj;
|
||||
|
||||
let diamond = new Object();
|
||||
let obj = new Object();
|
||||
obj.foo = "foo";
|
||||
obj.bar = 92;
|
||||
obj.backref = diamond;
|
||||
diamond.ref1 = obj;
|
||||
diamond.ref2 = obj;
|
||||
yield diamond;
|
||||
|
||||
let doubleref = new Object();
|
||||
let obj = new Object();
|
||||
doubleref.ref1 = obj;
|
||||
doubleref.ref2 = obj;
|
||||
yield doubleref;
|
||||
}
|
||||
|
|
|
@ -502,7 +502,6 @@ void
|
|||
nsHTMLEditor::AddPositioningOffset(PRInt32 & aX, PRInt32 & aY)
|
||||
{
|
||||
// Get the positioning offset
|
||||
nsresult res;
|
||||
PRInt32 positioningOffset =
|
||||
Preferences::GetInt("editor.positioning.offset", 0);
|
||||
|
||||
|
|
|
@ -148,6 +148,9 @@ SRGBOverrideObserver::Observe(nsISupports *aSubject,
|
|||
}
|
||||
|
||||
#define GFX_DOWNLOADABLE_FONTS_ENABLED "gfx.downloadable_fonts.enabled"
|
||||
#if defined(XP_MACOSX)
|
||||
#define GFX_DOWNLOADABLE_FONTS_ENABLED_LION "gfx.downloadable_fonts.enabled.lion"
|
||||
#endif
|
||||
#define GFX_DOWNLOADABLE_FONTS_SANITIZE "gfx.downloadable_fonts.sanitize"
|
||||
|
||||
#define GFX_PREF_HARFBUZZ_SCRIPTS "gfx.font_rendering.harfbuzz.scripts"
|
||||
|
@ -430,8 +433,21 @@ PRBool
|
|||
gfxPlatform::DownloadableFontsEnabled()
|
||||
{
|
||||
if (mAllowDownloadableFonts == UNINITIALIZED_VALUE) {
|
||||
#if defined(XP_MACOSX)
|
||||
// Work around a serious bug in how Apple handles downloaded fonts
|
||||
// on the most recent developer previews of OS X 10.7 (Lion, builds
|
||||
// 11A480b and 11A494a). See bug 663688.
|
||||
if (gfxPlatformMac::GetPlatform()->OSXVersion() >= 0x1070) {
|
||||
mAllowDownloadableFonts =
|
||||
Preferences::GetBool(GFX_DOWNLOADABLE_FONTS_ENABLED_LION, PR_FALSE);
|
||||
} else {
|
||||
mAllowDownloadableFonts =
|
||||
Preferences::GetBool(GFX_DOWNLOADABLE_FONTS_ENABLED, PR_FALSE);
|
||||
}
|
||||
#else
|
||||
mAllowDownloadableFonts =
|
||||
Preferences::GetBool(GFX_DOWNLOADABLE_FONTS_ENABLED, PR_FALSE);
|
||||
#endif
|
||||
}
|
||||
|
||||
return mAllowDownloadableFonts;
|
||||
|
|
|
@ -44,8 +44,6 @@
|
|||
#include "mozilla/jetpack/Handle.h"
|
||||
#include "mozilla/IntentionalCrash.h"
|
||||
|
||||
#include "jsarray.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
namespace mozilla {
|
||||
|
|
|
@ -150,7 +150,7 @@ _callHook(JSDContext *jsdc, JSContext *cx, JSStackFrame *fp, JSBool before,
|
|||
{
|
||||
if (before)
|
||||
{
|
||||
if (JSLL_IS_ZERO(pdata->lastCallStart))
|
||||
if (!pdata->lastCallStart)
|
||||
{
|
||||
int64 now;
|
||||
JSDProfileData *callerpdata;
|
||||
|
@ -166,13 +166,10 @@ _callHook(JSDContext *jsdc, JSContext *cx, JSStackFrame *fp, JSBool before,
|
|||
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)) {
|
||||
} else if (!pdata->recurseDepth && pdata->lastCallStart) {
|
||||
int64 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 = (JSFloat64) 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 = (JSFloat64) pdata->runningTime;
|
||||
delta /= 1000.0;
|
||||
}
|
||||
|
||||
|
|
|
@ -185,21 +185,22 @@ CPPSRCS = \
|
|||
sharkctl.cpp \
|
||||
GlobalObject.cpp \
|
||||
Stack.cpp \
|
||||
String.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 \
|
||||
js.msg \
|
||||
jsalloc.h \
|
||||
jsanalyze.h \
|
||||
jsapi.h \
|
||||
jsarray.h \
|
||||
jsarena.h \
|
||||
jsatom.h \
|
||||
jsbit.h \
|
||||
jsbool.h \
|
||||
jsclist.h \
|
||||
jsclone.h \
|
||||
jscntxt.h \
|
||||
|
@ -207,12 +208,10 @@ INSTALLED_HEADERS = \
|
|||
jsdate.h \
|
||||
jsdbgapi.h \
|
||||
jsdhash.h \
|
||||
jsdtoa.h \
|
||||
jsemit.h \
|
||||
jsfun.h \
|
||||
jsfriendapi.h \
|
||||
jsgc.h \
|
||||
jsgcmark.h \
|
||||
jscell.h \
|
||||
jsgcchunk.h \
|
||||
jsgcstats.h \
|
||||
|
@ -222,38 +221,28 @@ INSTALLED_HEADERS = \
|
|||
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 \
|
||||
|
@ -261,10 +250,8 @@ INSTALLED_HEADERS = \
|
|||
jstl.h \
|
||||
jshashtable.h \
|
||||
jsversion.h \
|
||||
jsweakmap.h \
|
||||
jswrapper.h \
|
||||
jsxdrapi.h \
|
||||
jsxml.h \
|
||||
jsval.h \
|
||||
jsvalue.h \
|
||||
prmjtime.h \
|
||||
|
@ -281,8 +268,9 @@ EXPORTS_NAMESPACES = vm
|
|||
|
||||
EXPORTS_vm = \
|
||||
ArgumentsObject.h \
|
||||
GlobalObject.h \
|
||||
Stack.h \
|
||||
GlobalObject.h \
|
||||
Stack.h \
|
||||
String.h \
|
||||
StringObject.h \
|
||||
$(NULL)
|
||||
|
||||
|
@ -641,7 +629,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/*" -not -path "*/config/*")
|
||||
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
|
||||
|
@ -657,7 +645,7 @@ 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:: 53 \
|
||||
$(srcdir)/config/check_source_count.py OffTheBooks:: 54 \
|
||||
"in Makefile.in" "{cx,rt}->{new_,new_array,malloc_,calloc_,realloc_}" $^
|
||||
# This should go to zero, if possible.
|
||||
$(srcdir)/config/check_source_count.py UnwantedForeground:: 34 \
|
||||
|
|
|
@ -42,11 +42,10 @@
|
|||
dup # any obj obj
|
||||
dup # any obj obj obj
|
||||
getprop valueOf # any obj obj valueOf
|
||||
ifprimtop 1 # any obj obj valueOf
|
||||
ifcantcalltop 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
|
||||
call 0 # any obj rval
|
||||
ifcantcalltop 3 # any obj rval
|
||||
pop # any obj
|
||||
dup # any obj obj
|
||||
goto 2
|
||||
|
@ -65,11 +64,10 @@
|
|||
dup # any obj obj
|
||||
dup # any obj obj obj
|
||||
getprop valueOf # any obj obj valueOf
|
||||
ifprimtop 1 # any obj obj valueOf
|
||||
ifcantcalltop 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
|
||||
call 0 # any obj lval
|
||||
ifcantcalltop 3 # any obj lval
|
||||
pop # any obj
|
||||
dup # any obj obj
|
||||
goto 2
|
||||
|
@ -93,11 +91,10 @@
|
|||
dup # any obj obj
|
||||
dup # any obj obj obj
|
||||
getprop valueOf # any obj obj valueOf
|
||||
ifprimtop 1 # any obj obj valueOf
|
||||
ifcantcalltop 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
|
||||
call 0 # any obj rval
|
||||
ifcantcalltop 3 # any obj rval
|
||||
pop # any obj
|
||||
dup # any obj obj
|
||||
goto 2
|
||||
|
@ -116,11 +113,10 @@
|
|||
dup # any obj obj
|
||||
dup # any obj obj obj
|
||||
getprop valueOf # any obj obj valueOf
|
||||
ifprimtop 1 # any obj obj valueOf
|
||||
ifcantcalltop 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
|
||||
call 0 # any obj lval
|
||||
ifcantcalltop 3 # any obj lval
|
||||
pop # any obj
|
||||
dup # any obj obj
|
||||
goto 2
|
||||
|
@ -140,11 +136,10 @@
|
|||
dup # obj2 obj1 obj1
|
||||
dup # obj2 obj1 obj1 obj1
|
||||
getprop valueOf # obj2 obj1 obj1 valueOf
|
||||
ifprimtop 1 # obj2 obj1 obj1 valueOf
|
||||
ifcantcalltop 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
|
||||
call 0 # obj2 obj1 lval
|
||||
ifcantcalltop 3 # obj2 obj1 lval
|
||||
pop # obj2 obj1
|
||||
dup # obj2 obj1 obj1
|
||||
goto 2
|
||||
|
@ -158,11 +153,10 @@
|
|||
dup # lval obj1 obj1
|
||||
dup # lval obj obj obj
|
||||
getprop valueOf # lval obj obj valueOf
|
||||
ifprimtop 4 # lval obj obj valueOf
|
||||
ifcantcalltop 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
|
||||
call 0 # lval obj rval
|
||||
ifcantcalltop 6 # lval obj rval
|
||||
pop # lval obj
|
||||
dup # lval obj obj
|
||||
goto 5
|
||||
|
@ -184,11 +178,10 @@
|
|||
dup # any obj obj
|
||||
dup # any obj obj obj
|
||||
getprop valueOf # any obj obj valueOf
|
||||
ifprimtop 1 # any obj obj valueOf
|
||||
ifcantcalltop 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
|
||||
call 0 # any obj rval
|
||||
ifcantcalltop 3 # any obj rval
|
||||
pop # any obj
|
||||
dup # any obj obj
|
||||
goto 2
|
||||
|
@ -207,11 +200,10 @@
|
|||
dup # any obj obj
|
||||
dup # any obj obj obj
|
||||
getprop valueOf # any obj obj valueOf
|
||||
ifprimtop 1 # any obj obj valueOf
|
||||
ifcantcalltop 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
|
||||
call 0 # any obj lval
|
||||
ifcantcalltop 3 # any obj lval
|
||||
pop # any obj
|
||||
dup # any obj obj
|
||||
goto 2
|
||||
|
@ -231,11 +223,10 @@
|
|||
dup # obj2 obj1 obj1
|
||||
dup # obj2 obj1 obj1 obj1
|
||||
getprop valueOf # obj2 obj1 obj1 valueOf
|
||||
ifprimtop 1 # obj2 obj1 obj1 valueOf
|
||||
ifcantcalltop 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
|
||||
call 0 # obj2 obj1 lval
|
||||
ifcantcalltop 3 # obj2 obj1 lval
|
||||
pop # obj2 obj1
|
||||
dup # obj2 obj1 obj1
|
||||
goto 2
|
||||
|
@ -249,11 +240,10 @@
|
|||
dup # lval obj obj
|
||||
dup # lval obj obj obj
|
||||
getprop valueOf # lval obj obj valueOf
|
||||
ifprimtop 4 # lval obj obj valueOf
|
||||
ifcantcalltop 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
|
||||
call 0 # lval obj rval
|
||||
ifcantcalltop 6 # lval obj rval
|
||||
pop # lval obj
|
||||
dup # lval obj obj
|
||||
goto 5
|
||||
|
@ -275,11 +265,10 @@
|
|||
dup # obj obj
|
||||
dup # obj obj obj
|
||||
getprop valueOf # obj obj valueOf
|
||||
ifprimtop 2 # obj obj valueOf
|
||||
ifcantcalltop 2 # obj obj valueOf
|
||||
swap # obj valueOf obj
|
||||
string number # obj valueOf obj "number"
|
||||
call 1 # obj lval
|
||||
ifprimtop 1 # obj lval
|
||||
call 0 # obj lval
|
||||
ifcantcalltop 1 # obj lval
|
||||
pop # obj
|
||||
dup # obj obj
|
||||
goto 3
|
||||
|
@ -358,17 +347,16 @@
|
|||
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
|
||||
ifcantcalltop 1 # String this obj obj toString
|
||||
swap # String this obj toString obj
|
||||
call 0 # String this obj rval
|
||||
ifprimtop 3 # String this obj rval
|
||||
ifcantcalltop 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
|
||||
call 0 # String this obj rval
|
||||
primtop (JSTYPE_STRING) # String this obj rval
|
||||
3: swap # String this rval obj
|
||||
pop # String this rval
|
||||
|
@ -384,17 +372,16 @@
|
|||
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
|
||||
ifcantcalltop 1 # String this obj obj toString
|
||||
swap # String this obj toString obj
|
||||
call 0 # String this obj rval
|
||||
ifprimtop 3 # String this obj rval
|
||||
ifcantcalltop 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
|
||||
call 0 # String this obj rval
|
||||
primtop (JSTYPE_STRING) # String this obj rval
|
||||
3: swap # String this rval obj
|
||||
pop # String this rval
|
||||
|
|
|
@ -98,6 +98,8 @@ class Test:
|
|||
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)
|
||||
|
||||
|
|
|
@ -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 InternalError, true);
|
||||
caught = true;
|
||||
}
|
||||
assertEq(caught, true);
|
||||
}
|
|
@ -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");
|
|
@ -0,0 +1,3 @@
|
|||
var obj = { toString: {}, valueOf: function() { return "foopy"; } };
|
||||
for (var i = 0; i < 25; i++)
|
||||
assertEq(String(obj), "foopy");
|
|
@ -0,0 +1,3 @@
|
|||
var obj = { valueOf: {}, toString: function() { return 0; } };
|
||||
for (var i = 0; i < 25; i++)
|
||||
assertEq(obj < 1, true);
|
|
@ -0,0 +1,7 @@
|
|||
for (var i = 0; i < 20; i++) {
|
||||
(function () {
|
||||
try {
|
||||
JSON.parse();
|
||||
} catch (e) {}
|
||||
}).call();
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
if (Proxy.fix) {
|
||||
var handler = { fix: function() { return []; } };
|
||||
var p = Proxy.createFunction(handler, function(){}, function(){});
|
||||
Proxy.fix(p);
|
||||
new p();
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
function f() {
|
||||
a = arguments;
|
||||
return a[0] - a[1];
|
||||
}
|
||||
|
||||
[1,2,3,4].sort(f);
|
|
@ -0,0 +1,11 @@
|
|||
// |jit-test| debug;mjit
|
||||
|
||||
/*
|
||||
* NOTE: this evalInFrame is explicitly exposing an optimization artifact that
|
||||
* InvokeSessionGuard leaves the callee frame on the stack between invocations.
|
||||
* If this ever gets fixed or InvokeSessionGuard gets removed, this test will
|
||||
* fail and it can be removed.
|
||||
*/
|
||||
o = { toString:function() { return evalInFrame(1, "arguments; x") } }
|
||||
var s = "aaaaaaaaaa".replace(/a/g, function() { var x = 'B'; return o });
|
||||
assertEq(s, "BBBBBBBBBB");
|
|
@ -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);
|
|
@ -1 +0,0 @@
|
|||
Binary file (standard input) matches
|
|
@ -349,4 +349,5 @@ 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_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")
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -52,6 +52,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 +65,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 +79,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 +90,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 +103,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);
|
||||
|
|
|
@ -464,7 +464,7 @@ Script::analyze(JSContext *cx, JSScript *script)
|
|||
/* Watch for opcodes the method JIT doesn't compile. */
|
||||
case JSOP_GOSUB:
|
||||
case JSOP_GOSUBX:
|
||||
case JSOP_IFPRIMTOP:
|
||||
case JSOP_IFCANTCALLTOP:
|
||||
case JSOP_FILTER:
|
||||
case JSOP_ENDFILTER:
|
||||
case JSOP_TABLESWITCHX:
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include "tests.h"
|
||||
#include "jsobj.h"
|
||||
#include "jsstr.h"
|
||||
#include "vm/String.h"
|
||||
|
||||
BEGIN_TEST(testConservativeGC)
|
||||
{
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*/
|
||||
|
||||
#include "tests.h"
|
||||
#include "jsstr.h"
|
||||
#include "vm/String.h"
|
||||
|
||||
BEGIN_TEST(testIntString_bug515273)
|
||||
{
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
#include "tests.h"
|
||||
#include "jsstr.h"
|
||||
#include "vm/String.h"
|
||||
|
||||
using namespace js;
|
||||
|
||||
|
|
|
@ -94,10 +94,10 @@
|
|||
#include "jsscopeinlines.h"
|
||||
#include "jsregexpinlines.h"
|
||||
#include "jsscriptinlines.h"
|
||||
#include "jsstrinlines.h"
|
||||
#include "assembler/wtf/Platform.h"
|
||||
|
||||
#include "vm/Stack-inl.h"
|
||||
#include "vm/String-inl.h"
|
||||
|
||||
#if ENABLE_YARR_JIT
|
||||
#include "assembler/jit/ExecutableAllocator.h"
|
||||
|
@ -684,7 +684,7 @@ JSRuntime::init(uint32 maxbytes)
|
|||
return false;
|
||||
}
|
||||
|
||||
atomsCompartment->setGCLastBytes(8192);
|
||||
atomsCompartment->setGCLastBytes(8192, GC_NORMAL);
|
||||
|
||||
if (!js_InitAtomState(this))
|
||||
return false;
|
||||
|
@ -707,7 +707,15 @@ JSRuntime::init(uint32 maxbytes)
|
|||
#endif
|
||||
|
||||
debugMode = JS_FALSE;
|
||||
return js_InitThreads(this);
|
||||
|
||||
if (!js_InitThreads(this))
|
||||
return false;
|
||||
if (!InitRuntimeNumberState(this))
|
||||
return false;
|
||||
if (!InitRuntimeScriptState(this))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
JSRuntime::~JSRuntime()
|
||||
|
@ -733,8 +741,9 @@ JSRuntime::~JSRuntime()
|
|||
FinishJIT();
|
||||
#endif
|
||||
|
||||
FreeRuntimeScriptState(this);
|
||||
FinishRuntimeNumberState(this);
|
||||
js_FinishThreads(this);
|
||||
js_FreeRuntimeScriptState(this);
|
||||
js_FinishAtomState(this);
|
||||
|
||||
js_FinishGC(this);
|
||||
|
@ -1604,8 +1613,8 @@ 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},
|
||||
{NULL, 0, NULL, NULL}
|
||||
};
|
||||
|
||||
|
@ -1657,7 +1666,7 @@ static JSStdName standard_class_names[] = {
|
|||
#endif
|
||||
|
||||
/* Typed Arrays */
|
||||
{js_InitTypedArrayClasses, EAGER_CLASS_ATOM(ArrayBuffer), &js::ArrayBuffer::jsclass},
|
||||
{js_InitTypedArrayClasses, EAGER_CLASS_ATOM(ArrayBuffer), &js::ArrayBuffer::fastClass},
|
||||
{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)},
|
||||
|
@ -2852,7 +2861,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, Valueify(vp));
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
|
|
|
@ -971,7 +971,7 @@ Class js_ArrayClass = {
|
|||
StrictPropertyStub, /* setProperty */
|
||||
EnumerateStub,
|
||||
ResolveStub,
|
||||
js_TryValueOf,
|
||||
ConvertStub,
|
||||
NULL,
|
||||
NULL, /* reserved0 */
|
||||
NULL, /* checkAccess */
|
||||
|
@ -1007,7 +1007,7 @@ Class js_SlowArrayClass = {
|
|||
StrictPropertyStub, /* setProperty */
|
||||
EnumerateStub,
|
||||
ResolveStub,
|
||||
js_TryValueOf
|
||||
ConvertStub
|
||||
};
|
||||
|
||||
static bool
|
||||
|
@ -3205,131 +3205,3 @@ js_ArrayInfo(JSContext *cx, uintN argc, jsval *vp)
|
|||
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;
|
||||
}
|
||||
|
|
|
@ -46,7 +46,6 @@
|
|||
#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;
|
||||
|
@ -257,26 +256,6 @@ js_ArrayInfo(JSContext *cx, uintN argc, jsval *vp);
|
|||
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.
|
||||
*/
|
||||
|
||||
JS_FRIEND_API(JSBool)
|
||||
js_CoerceArrayToCanvasImageData(JSObject *obj, jsuint offset, jsuint count,
|
||||
JSUint8 *dest);
|
||||
|
||||
JSBool
|
||||
js_PrototypeHasIndexedProperties(JSContext *cx, JSObject *obj);
|
||||
|
||||
|
@ -291,26 +270,6 @@ 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);
|
||||
|
||||
|
|
|
@ -64,6 +64,8 @@
|
|||
#include "jsatominlines.h"
|
||||
#include "jsobjinlines.h"
|
||||
|
||||
#include "vm/String-inl.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::gc;
|
||||
|
||||
|
@ -201,7 +203,9 @@ const char *const js_common_atom_names[] = {
|
|||
"keys", /* keysAtom */
|
||||
"iterate", /* iterateAtom */
|
||||
|
||||
"WeakMap" /* WeakMapAtom */
|
||||
"WeakMap", /* WeakMapAtom */
|
||||
|
||||
"byteLength" /* byteLengthAtom */
|
||||
};
|
||||
|
||||
void
|
||||
|
|
|
@ -48,12 +48,13 @@
|
|||
#include "jsprvtd.h"
|
||||
#include "jshash.h"
|
||||
#include "jshashtable.h"
|
||||
#include "jsnum.h"
|
||||
#include "jspubtd.h"
|
||||
#include "jsstr.h"
|
||||
#include "jslock.h"
|
||||
#include "jsvalue.h"
|
||||
|
||||
#include "vm/String.h"
|
||||
|
||||
/* Engine-internal extensions of jsid */
|
||||
|
||||
static JS_ALWAYS_INLINE jsid
|
||||
|
@ -111,16 +112,6 @@ 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));
|
||||
}
|
||||
|
||||
template<>
|
||||
struct DefaultHasher<jsid>
|
||||
{
|
||||
|
@ -468,6 +459,8 @@ struct JSAtomState
|
|||
|
||||
JSAtom *WeakMapAtom;
|
||||
|
||||
JSAtom *byteLengthAtom;
|
||||
|
||||
/* Less frequently used atoms, pinned lazily by JS_ResolveStandardClass. */
|
||||
struct {
|
||||
JSAtom *XMLListAtom;
|
||||
|
|
|
@ -151,6 +151,16 @@ IndexToId(JSContext *cx, uint32 index, jsid *idp)
|
|||
return true;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
} // namespace js
|
||||
|
||||
#endif /* jsatominlines_h___ */
|
||||
|
|
|
@ -45,7 +45,6 @@
|
|||
|
||||
#include "jsapi.h"
|
||||
#include "jsobj.h"
|
||||
#include "jsstr.h"
|
||||
|
||||
extern js::Class js_BooleanClass;
|
||||
|
||||
|
|
|
@ -61,9 +61,10 @@
|
|||
#include "jsvector.h"
|
||||
|
||||
#include "jsatominlines.h"
|
||||
#include "jscntxtinlines.h"
|
||||
#include "jsnuminlines.h"
|
||||
#include "jsobjinlines.h"
|
||||
#include "jsscopeinlines.h"
|
||||
#include "jscntxtinlines.h"
|
||||
|
||||
using namespace avmplus;
|
||||
using namespace nanojit;
|
||||
|
|
|
@ -40,6 +40,8 @@
|
|||
#ifndef jscell_h___
|
||||
#define jscell_h___
|
||||
|
||||
#include "jspubtd.h"
|
||||
|
||||
struct JSCompartment;
|
||||
|
||||
namespace js {
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
#include "jstypedarray.h"
|
||||
|
||||
#include "jsregexpinlines.h"
|
||||
#include "jstypedarrayinlines.h"
|
||||
|
||||
using namespace js;
|
||||
|
||||
|
@ -84,6 +85,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
|
||||
|
@ -390,7 +392,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()));
|
||||
|
@ -457,9 +458,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, ArrayBuffer::getByteLength(obj)) &&
|
||||
out.writeBytes(ArrayBuffer::getDataOffset(obj), ArrayBuffer::getByteLength(obj));
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -467,18 +468,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.count() == UINT32_MAX) {
|
||||
JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL,
|
||||
JSMSG_NEED_DIET, "object graph to serialize");
|
||||
return false;
|
||||
}
|
||||
if (!memory.add(p, obj))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Get enumerable property ids and put them in reverse order so that they
|
||||
|
@ -526,7 +527,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());
|
||||
|
@ -574,17 +575,21 @@ JSStructuredCloneWriter::write(const Value &v)
|
|||
|
||||
if (prop) {
|
||||
Value val;
|
||||
if (!writeId(id) || !obj->getProperty(context(), id, &val) || !startWrite(val))
|
||||
if (!writeId(id) ||
|
||||
!obj->getProperty(context(), id, &val) ||
|
||||
!startWrite(val))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
out.writePair(SCTAG_NULL, 0);
|
||||
memory.remove(obj);
|
||||
objs.popBack();
|
||||
counts.popBack();
|
||||
}
|
||||
}
|
||||
|
||||
memory.clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -679,9 +684,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(ArrayBuffer::getByteLength(obj) == nbytes);
|
||||
return in.readArray(ArrayBuffer::getDataOffset(obj), nbytes);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -772,12 +776,23 @@ JSStructuredCloneReader::startRead(Value *vp)
|
|||
JSObject *obj = (tag == SCTAG_ARRAY_OBJECT)
|
||||
? NewDenseEmptyArray(context())
|
||||
: NewBuiltinClassInstance(context(), &js_ObjectClass);
|
||||
if (!obj || !objs.append(ObjectValue(*obj)))
|
||||
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);
|
||||
|
||||
|
@ -857,5 +872,8 @@ JSStructuredCloneReader::read(Value *vp)
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
allObjs.clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -113,7 +113,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 +134,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 +171,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 +182,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<JSObject *> MemorySet;
|
||||
MemorySet memory;
|
||||
// memory is a superset of objs; items are never removed from Memory
|
||||
// until a serialization operation is finished
|
||||
typedef js::HashMap<JSObject *, uint32> CloneMemory;
|
||||
CloneMemory memory;
|
||||
|
||||
// The user defined callbacks that will be used for cloning.
|
||||
const JSStructuredCloneCallbacks *callbacks;
|
||||
|
|
|
@ -62,6 +62,7 @@
|
|||
#include "jscntxt.h"
|
||||
#include "jsversion.h"
|
||||
#include "jsdbgapi.h"
|
||||
#include "jsdtoa.h"
|
||||
#include "jsexn.h"
|
||||
#include "jsfun.h"
|
||||
#include "jsgc.h"
|
||||
|
@ -300,7 +301,7 @@ JSContext *
|
|||
js_NewContext(JSRuntime *rt, size_t stackChunkSize)
|
||||
{
|
||||
JSContext *cx;
|
||||
JSBool ok, first;
|
||||
JSBool first;
|
||||
JSContextCallback cxCallback;
|
||||
|
||||
/*
|
||||
|
@ -317,6 +318,7 @@ js_NewContext(JSRuntime *rt, size_t stackChunkSize)
|
|||
#if JS_STACK_GROWTH_DIRECTION > 0
|
||||
cx->stackLimit = (jsuword) -1;
|
||||
#endif
|
||||
cx->iterValue.setMagic(JS_NO_ITER_VALUE);
|
||||
JS_STATIC_ASSERT(JSVERSION_DEFAULT == 0);
|
||||
JS_ASSERT(cx->findVersion() == JSVERSION_DEFAULT);
|
||||
VOUCH_DOES_NOT_REQUIRE_STACK();
|
||||
|
@ -382,17 +384,7 @@ 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);
|
||||
if (ok)
|
||||
ok = js_InitRuntimeNumberState(cx);
|
||||
JSBool ok = js_InitCommonAtoms(cx);
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
JS_EndRequest(cx);
|
||||
|
@ -614,8 +606,6 @@ js_DestroyContext(JSContext *cx, JSDestroyContextMode mode)
|
|||
JS_BeginRequest(cx);
|
||||
#endif
|
||||
|
||||
js_FinishRuntimeNumberState(cx);
|
||||
|
||||
/* Unpin all common atoms before final GC. */
|
||||
js_FinishCommonAtoms(cx);
|
||||
|
||||
|
@ -681,7 +671,7 @@ js_ContextIterator(JSRuntime *rt, JSBool unlocked, JSContext **iterp)
|
|||
JSContext *cx = *iterp;
|
||||
|
||||
Maybe<AutoLockGC> lockIf;
|
||||
if (unlocked)
|
||||
if (unlocked)
|
||||
lockIf.construct(rt);
|
||||
cx = js_ContextFromLinkField(cx ? cx->link.next : rt->contextList.next);
|
||||
if (&cx->link == &rt->contextList)
|
||||
|
@ -1461,6 +1451,9 @@ JSContext::JSContext(JSRuntime *rt)
|
|||
compartment(NULL),
|
||||
stack(thisDuringConstruction()),
|
||||
busyArrays()
|
||||
#ifdef DEBUG
|
||||
, stackIterAssertionEnabled(true)
|
||||
#endif
|
||||
{}
|
||||
|
||||
JSContext::~JSContext()
|
||||
|
@ -1721,4 +1714,15 @@ LeaveTrace(JSContext *cx)
|
|||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
CanLeaveTrace(JSContext *cx)
|
||||
{
|
||||
JS_ASSERT(JS_ON_TRACE(cx));
|
||||
#ifdef JS_TRACER
|
||||
return JS_TRACE_MONITOR_ON_TRACE(cx)->bailExit != NULL;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
} /* namespace js */
|
||||
|
|
|
@ -48,22 +48,18 @@
|
|||
#include "jsprvtd.h"
|
||||
#include "jsarena.h"
|
||||
#include "jsclist.h"
|
||||
#include "jslong.h"
|
||||
#include "jsatom.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"
|
||||
|
||||
|
@ -88,6 +84,10 @@ template<typename T> class Seq;
|
|||
|
||||
} /* namespace nanojit */
|
||||
|
||||
JS_BEGIN_EXTERN_C
|
||||
struct DtoaState;
|
||||
JS_END_EXTERN_C
|
||||
|
||||
namespace js {
|
||||
|
||||
/* Tracer constants. */
|
||||
|
@ -117,6 +117,8 @@ namespace mjit {
|
|||
class JaegerCompartment;
|
||||
}
|
||||
|
||||
class WeakMapBase;
|
||||
|
||||
/*
|
||||
* 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,
|
||||
|
@ -207,7 +209,7 @@ struct ThreadData {
|
|||
/* Property cache for faster call/get/set invocation. */
|
||||
PropertyCache propertyCache;
|
||||
|
||||
/* State used by dtoa.c. */
|
||||
/* State used by jsdtoa.cpp. */
|
||||
DtoaState *dtoaState;
|
||||
|
||||
/* Base address of the native stack for the current thread. */
|
||||
|
@ -420,10 +422,12 @@ struct JSRuntime {
|
|||
uint32 gcEmptyArenaPoolLifespan;
|
||||
uint32 gcNumber;
|
||||
js::GCMarker *gcMarkingTracer;
|
||||
bool gcChunkAllocationSinceLastGC;
|
||||
int64 gcNextFullGCTime;
|
||||
int64 gcJitReleaseTime;
|
||||
JSGCMode gcMode;
|
||||
volatile bool gcIsNeeded;
|
||||
JSObject *gcWeakMapList;
|
||||
js::WeakMapBase *gcWeakMapList;
|
||||
|
||||
/* Pre-allocated space for the GC mark stacks. Pointer type ensures alignment. */
|
||||
void *gcMarkStackObjs[js::OBJECT_MARK_STACK_SIZE / sizeof(void *)];
|
||||
|
@ -785,7 +789,7 @@ struct JSRuntime {
|
|||
|
||||
bool init(uint32 maxbytes);
|
||||
|
||||
void setGCLastBytes(size_t lastBytes);
|
||||
void setGCLastBytes(size_t lastBytes, JSGCInvocationKind gckind);
|
||||
void reduceGCTriggerBytes(uint32 amount);
|
||||
|
||||
/*
|
||||
|
@ -1412,6 +1416,14 @@ struct JSContext
|
|||
this->exception.setUndefined();
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
/*
|
||||
* Controls whether a quadratic-complexity assertion is performed during
|
||||
* stack iteration, defaults to true.
|
||||
*/
|
||||
bool stackIterAssertionEnabled;
|
||||
#endif
|
||||
|
||||
private:
|
||||
/*
|
||||
* The allocation code calls the function to indicate either OOM failure
|
||||
|
@ -2271,7 +2283,7 @@ static JS_INLINE JSContext *
|
|||
js_ContextFromLinkField(JSCList *link)
|
||||
{
|
||||
JS_ASSERT(link);
|
||||
return (JSContext *) ((uint8 *) link - offsetof(JSContext, link));
|
||||
return reinterpret_cast<JSContext *>(uintptr_t(link) - offsetof(JSContext, link));
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2438,6 +2450,9 @@ class RegExpStatics;
|
|||
extern JS_FORCES_STACK JS_FRIEND_API(void)
|
||||
LeaveTrace(JSContext *cx);
|
||||
|
||||
extern bool
|
||||
CanLeaveTrace(JSContext *cx);
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
/*
|
||||
|
@ -2519,6 +2534,8 @@ class AutoVectorRooter : protected AutoGCRooter
|
|||
return true;
|
||||
}
|
||||
|
||||
void clear() { vector.clear(); }
|
||||
|
||||
bool reserve(size_t newLength) {
|
||||
return vector.reserve(newLength);
|
||||
}
|
||||
|
@ -2590,6 +2607,46 @@ class AutoShapeVector : public AutoVectorRooter<const Shape *>
|
|||
JSIdArray *
|
||||
NewIdArray(JSContext *cx, jsint length);
|
||||
|
||||
/*
|
||||
* 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
|
||||
{
|
||||
JSRuntime *const runtime;
|
||||
|
||||
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 {}
|
||||
};
|
||||
|
||||
/*
|
||||
* 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 */
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
|
|
@ -317,7 +317,10 @@ CallJSNativeConstructor(JSContext *cx, js::Native native, const CallArgs &args)
|
|||
* (new Object(Object)) returns the callee.
|
||||
*/
|
||||
extern JSBool proxy_Construct(JSContext *, uintN, Value *);
|
||||
JS_ASSERT_IF(native != proxy_Construct && native != js::CallOrConstructBoundFunction &&
|
||||
extern JSBool callable_Construct(JSContext *, uintN, Value *);
|
||||
JS_ASSERT_IF(native != proxy_Construct &&
|
||||
native != callable_Construct &&
|
||||
native != js::CallOrConstructBoundFunction &&
|
||||
(!callee.isFunction() || callee.getFunctionPrivate()->u.n.clasp != &js_ObjectClass),
|
||||
!args.rval().isPrimitive() && callee != args.rval().toObject());
|
||||
|
||||
|
@ -383,17 +386,6 @@ LeaveTraceIfArgumentsObject(JSContext *cx, JSObject *obj)
|
|||
LeaveTrace(cx);
|
||||
}
|
||||
|
||||
static JS_INLINE JSBool
|
||||
CanLeaveTrace(JSContext *cx)
|
||||
{
|
||||
JS_ASSERT(JS_ON_TRACE(cx));
|
||||
#ifdef JS_TRACER
|
||||
return JS_TRACE_MONITOR_ON_TRACE(cx)->bailExit != NULL;
|
||||
#else
|
||||
return JS_FALSE;
|
||||
#endif
|
||||
}
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
#ifdef JS_METHODJIT
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
#include "jsgc.h"
|
||||
#include "jsgcmark.h"
|
||||
#include "jsiter.h"
|
||||
#include "jsmath.h"
|
||||
#include "jsproxy.h"
|
||||
#include "jsscope.h"
|
||||
#include "jstracer.h"
|
||||
|
|
|
@ -42,12 +42,10 @@
|
|||
|
||||
#include "jscntxt.h"
|
||||
#include "jsgc.h"
|
||||
#include "jsmath.h"
|
||||
#include "jsobj.h"
|
||||
#include "jsfun.h"
|
||||
#include "jsgcstats.h"
|
||||
#include "jsclist.h"
|
||||
#include "jsxml.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
|
@ -347,6 +345,8 @@ class NativeIterCache {
|
|||
}
|
||||
};
|
||||
|
||||
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
|
||||
|
@ -493,7 +493,7 @@ struct JS_FRIEND_API(JSCompartment) {
|
|||
void finalizeShapeArenaLists(JSContext *cx);
|
||||
bool arenaListsAreEmpty();
|
||||
|
||||
void setGCLastBytes(size_t lastBytes);
|
||||
void setGCLastBytes(size_t lastBytes, JSGCInvocationKind gckind);
|
||||
void reduceGCTriggerBytes(uint32 amount);
|
||||
|
||||
js::DtoaCache dtoaCache;
|
||||
|
|
|
@ -45,7 +45,6 @@
|
|||
* the NSPR typedef names may be.
|
||||
*/
|
||||
#include "jstypes.h"
|
||||
#include "jslong.h"
|
||||
|
||||
typedef JSIntn intN;
|
||||
typedef JSUintn uintN;
|
||||
|
|
|
@ -487,6 +487,15 @@ 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
|
||||
*/
|
||||
|
@ -501,7 +510,7 @@ Class js_DateClass = {
|
|||
StrictPropertyStub, /* setProperty */
|
||||
EnumerateStub,
|
||||
ResolveStub,
|
||||
ConvertStub
|
||||
date_convert
|
||||
};
|
||||
|
||||
/* for use by date_parse */
|
||||
|
@ -2083,8 +2092,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. */
|
||||
|
|
|
@ -117,9 +117,9 @@ JSCodeGenerator::JSCodeGenerator(Parser *parser,
|
|||
emitLevel(0),
|
||||
constMap(parser->context),
|
||||
constList(parser->context),
|
||||
globalUses(ContextAllocPolicy(parser->context)),
|
||||
closedArgs(ContextAllocPolicy(parser->context)),
|
||||
closedVars(ContextAllocPolicy(parser->context)),
|
||||
globalUses(parser->context),
|
||||
closedArgs(parser->context),
|
||||
closedVars(parser->context),
|
||||
traceIndex(0)
|
||||
{
|
||||
flags = TCF_COMPILING;
|
||||
|
@ -5809,12 +5809,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
|||
|
||||
#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;
|
||||
}
|
||||
JS_ASSERT(cg->inFunction());
|
||||
if (pn->pn_kid) {
|
||||
if (!js_EmitTree(cx, cg, pn->pn_kid))
|
||||
return JS_FALSE;
|
||||
|
|
|
@ -301,6 +301,11 @@ 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 */
|
||||
uint32 parenDepth; /* paren-nesting depth */
|
||||
uint32 yieldCount; /* number of |yield| tokens encountered at
|
||||
non-zero depth in current paren tree */
|
||||
uint32 argumentsCount; /* number of |arguments| references encountered
|
||||
at non-zero depth in current paren tree */
|
||||
JSStmtInfo *topStmt; /* top of statement info stack */
|
||||
JSStmtInfo *topScopeStmt; /* top lexical scope statement */
|
||||
JSObjectBox *blockChainBox; /* compile time block scope chain (NB: one
|
||||
|
@ -310,6 +315,12 @@ struct JSTreeContext { /* tree context for semantic checks */
|
|||
(block with its own lexical scope) */
|
||||
JSAtomList decls; /* function, const, and var declarations */
|
||||
js::Parser *parser; /* ptr to common parsing and lexing data */
|
||||
JSParseNode *yieldNode; /* parse node for a yield expression that might
|
||||
be an error if we turn out to be inside a
|
||||
generator expression */
|
||||
JSParseNode *argumentsNode; /* parse node for an arguments variable that
|
||||
might be an error if we turn out to be
|
||||
inside a generator expression */
|
||||
|
||||
private:
|
||||
union {
|
||||
|
@ -358,9 +369,11 @@ struct JSTreeContext { /* tree context for semantic checks */
|
|||
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),
|
||||
: flags(0), bodyid(0), blockidGen(0), parenDepth(0), yieldCount(0), argumentsCount(0),
|
||||
topStmt(NULL), topScopeStmt(NULL),
|
||||
blockChainBox(NULL), blockNode(NULL), parser(prs),
|
||||
yieldNode(NULL), argumentsNode(NULL),
|
||||
scopeChain_(NULL), parent(prs->tc), staticLevel(0), funbox(NULL), functionList(NULL),
|
||||
innermostWith(NULL), bindings(prs->context, prs->emptyCallShape),
|
||||
sharpSlotBase(-1)
|
||||
{
|
||||
|
@ -458,13 +471,20 @@ struct JSTreeContext { /* tree context for semantic checks */
|
|||
return flags & TCF_FUN_MUTATES_PARAMETER;
|
||||
}
|
||||
|
||||
void noteArgumentsUse() {
|
||||
void noteArgumentsUse(JSParseNode *pn) {
|
||||
JS_ASSERT(inFunction());
|
||||
countArgumentsUse(pn);
|
||||
flags |= TCF_FUN_USES_ARGUMENTS;
|
||||
if (funbox)
|
||||
funbox->node->pn_dflags |= PND_FUNARG;
|
||||
}
|
||||
|
||||
void countArgumentsUse(JSParseNode *pn) {
|
||||
JS_ASSERT(pn->pn_atom == parser->context->runtime->atomState.argumentsAtom);
|
||||
argumentsCount++;
|
||||
argumentsNode = pn;
|
||||
}
|
||||
|
||||
bool needsEagerArguments() const {
|
||||
return inStrictMode() && ((usesArguments() && mutatesParameter()) || callsEval());
|
||||
}
|
||||
|
@ -625,13 +645,13 @@ struct JSCodeGenerator : public JSTreeContext
|
|||
JSAtomList upvarList; /* map of atoms to upvar indexes */
|
||||
JSUpvarArray upvarMap; /* indexed upvar pairs (JS_realloc'ed) */
|
||||
|
||||
typedef js::Vector<js::GlobalSlotArray::Entry, 16, js::ContextAllocPolicy> GlobalUseVector;
|
||||
typedef js::Vector<js::GlobalSlotArray::Entry, 16> 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<uint32, 8, js::ContextAllocPolicy> SlotVector;
|
||||
typedef js::Vector<uint32, 8> SlotVector;
|
||||
SlotVector closedArgs;
|
||||
SlotVector closedVars;
|
||||
|
||||
|
|
164
js/src/jsfun.cpp
164
js/src/jsfun.cpp
|
@ -1539,101 +1539,56 @@ StackFrame::getValidCalleeObject(JSContext *cx, Value *vp)
|
|||
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();
|
||||
|
||||
/* Set to early to null in case of error */
|
||||
vp->setNull();
|
||||
|
||||
/* Find fun's top-most activation record. */
|
||||
StackFrame *fp = js_GetTopStackFrame(cx);
|
||||
while (fp && (fp->maybeFun() != fun || fp->isEvalInFunction()))
|
||||
while (!fp->isFunctionFrame() || fp->fun() != fun) {
|
||||
fp = fp->prev();
|
||||
if (!fp)
|
||||
return true;
|
||||
}
|
||||
|
||||
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)) {
|
||||
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;
|
||||
}
|
||||
if (fp) {
|
||||
if (!js_GetArgsValue(cx, fp, vp))
|
||||
if (!js_GetArgsValue(cx, fp, vp))
|
||||
return false;
|
||||
} else {
|
||||
vp->setNull();
|
||||
}
|
||||
break;
|
||||
|
||||
case FUN_LENGTH:
|
||||
case FUN_ARITY:
|
||||
vp->setInt32(fun->nargs);
|
||||
break;
|
||||
|
||||
case FUN_NAME:
|
||||
vp->setString(fun->atom ? fun->atom
|
||||
: cx->runtime->emptyString);
|
||||
break;
|
||||
|
||||
case FUN_CALLER: {
|
||||
vp->setNull();
|
||||
|
||||
StackFrame *callerframe = (fp && fp->prev()) ? js_GetScriptedCaller(cx, fp->prev()) : NULL;
|
||||
if (callerframe && !callerframe->getValidCalleeObject(cx, vp))
|
||||
} else if (JSID_IS_ATOM(id, cx->runtime->atomState.callerAtom)) {
|
||||
if (!fp->prev())
|
||||
return false;
|
||||
|
||||
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.compartment() != cx->compartment) {
|
||||
vp->setNull();
|
||||
} else if (caller.isFunction()) {
|
||||
} else if (caller.isFunction()) {
|
||||
JSFunction *callerFun = caller.getFunctionPrivate();
|
||||
if (callerFun->isInterpreted() && callerFun->inStrictMode()) {
|
||||
JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL,
|
||||
|
@ -1642,38 +1597,20 @@ fun_getProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
|
|||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
} else {
|
||||
JS_NOT_REACHED("fun_getProperty");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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 JS_ARRAY_LENGTH to bound loops.
|
||||
* Properties censored into [[ThrowTypeError]] in strict mode. */
|
||||
static const uint16 poisonPillProps[] = {
|
||||
ATOM_OFFSET(arguments),
|
||||
ATOM_OFFSET(caller),
|
||||
};
|
||||
|
||||
static JSBool
|
||||
|
@ -1693,17 +1630,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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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(poisonPillProps); i++) {
|
||||
const PoisonPillProp &p = poisonPillProps[i];
|
||||
id = ATOM_TO_JSID(OFFSET_TO_ATOM(cx->runtime, p.atomOffset));
|
||||
const uint16 offset = poisonPillProps[i];
|
||||
id = ATOM_TO_JSID(OFFSET_TO_ATOM(cx->runtime, offset));
|
||||
if (!obj->hasProperty(cx, id, &found, JSRESOLVE_QUALIFIED))
|
||||
return false;
|
||||
}
|
||||
|
@ -1785,10 +1719,17 @@ fun_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
|
|||
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 (!DefineNativeProperty(cx, obj, id, Int32Value(fun->nargs),
|
||||
PropertyStub, StrictPropertyStub,
|
||||
|
||||
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, obj, id, v, PropertyStub, StrictPropertyStub,
|
||||
JSPROP_PERMANENT | JSPROP_READONLY, 0, 0)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -1796,25 +1737,10 @@ fun_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
|
|||
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 (!DefineNativeProperty(cx, obj, id, UndefinedValue(),
|
||||
fun_getProperty, StrictPropertyStub,
|
||||
lfp->attrs, Shape::HAS_SHORTID, lfp->tinyid)) {
|
||||
return false;
|
||||
}
|
||||
*objp = obj;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (uintN i = 0; i < JS_ARRAY_LENGTH(poisonPillProps); i++) {
|
||||
const PoisonPillProp &p = poisonPillProps[i];
|
||||
const uint16 offset = poisonPillProps[i];
|
||||
|
||||
if (JSID_IS_ATOM(id, OFFSET_TO_ATOM(cx->runtime, p.atomOffset))) {
|
||||
if (JSID_IS_ATOM(id, OFFSET_TO_ATOM(cx->runtime, offset))) {
|
||||
JS_ASSERT(!IsInternalFunctionObject(obj));
|
||||
|
||||
PropertyOp getter;
|
||||
|
@ -1832,7 +1758,7 @@ fun_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
|
|||
}
|
||||
|
||||
if (!DefineNativeProperty(cx, obj, id, UndefinedValue(), getter, setter,
|
||||
attrs, Shape::HAS_SHORTID, p.tinyid)) {
|
||||
attrs, 0, 0)) {
|
||||
return false;
|
||||
}
|
||||
*objp = obj;
|
||||
|
@ -2436,7 +2362,7 @@ Function(JSContext *cx, uintN argc, Value *vp)
|
|||
|
||||
/* Block this call if security callbacks forbid it. */
|
||||
GlobalObject *global = call.callee().getGlobal();
|
||||
if (!global->isEvalAllowed(cx)) {
|
||||
if (!global->isRuntimeCodeGenEnabled(cx)) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CSP_BLOCKED_FUNCTION);
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -213,8 +213,9 @@ struct JSFunction : public JSObject_Slots2
|
|||
getSlotRef(METHOD_ATOM_SLOT).setString(atom);
|
||||
}
|
||||
|
||||
js::Native maybeNative() const {
|
||||
return isInterpreted() ? NULL : u.n.native;
|
||||
JSScript *script() const {
|
||||
JS_ASSERT(isInterpreted());
|
||||
return u.i.script;
|
||||
}
|
||||
|
||||
js::Native native() const {
|
||||
|
@ -222,9 +223,8 @@ struct JSFunction : public JSObject_Slots2
|
|||
return u.n.native;
|
||||
}
|
||||
|
||||
JSScript *script() const {
|
||||
JS_ASSERT(isInterpreted());
|
||||
return u.i.script;
|
||||
js::Native maybeNative() const {
|
||||
return isInterpreted() ? NULL : native();
|
||||
}
|
||||
|
||||
static uintN offsetOfNativeOrScript() {
|
||||
|
@ -324,6 +324,36 @@ IsNativeFunction(const js::Value &v, JSFunction **fun)
|
|||
return IsFunctionObject(v, fun) && (*fun)->isNative();
|
||||
}
|
||||
|
||||
static JS_ALWAYS_INLINE bool
|
||||
IsNativeFunction(const js::Value &v, Native 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 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)
|
||||
{
|
||||
JS_ASSERT(obj->getClass() == clasp);
|
||||
|
||||
Value v;
|
||||
if (!HasDataProperty(obj, methodid, &v)) {
|
||||
JSObject *proto = obj->getProto();
|
||||
if (!proto || proto->getClass() != clasp || !HasDataProperty(proto, methodid, &v))
|
||||
return false;
|
||||
}
|
||||
|
||||
return js::IsNativeFunction(v, native);
|
||||
}
|
||||
|
||||
extern JS_ALWAYS_INLINE bool
|
||||
SameTraceType(const Value &lhs, const Value &rhs)
|
||||
{
|
||||
|
|
366
js/src/jsgc.cpp
366
js/src/jsgc.cpp
|
@ -59,6 +59,7 @@
|
|||
#include "jsprf.h"
|
||||
#include "jsapi.h"
|
||||
#include "jsatom.h"
|
||||
#include "jscompartment.h"
|
||||
#include "jscntxt.h"
|
||||
#include "jsversion.h"
|
||||
#include "jsdbgapi.h"
|
||||
|
@ -67,30 +68,29 @@
|
|||
#include "jsgc.h"
|
||||
#include "jsgcchunk.h"
|
||||
#include "jsgcmark.h"
|
||||
#include "jshashtable.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 "jsweakmap.h"
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
#include "jsxml.h"
|
||||
#endif
|
||||
|
||||
#include "jsprobes.h"
|
||||
#include "jsobjinlines.h"
|
||||
#include "jshashtable.h"
|
||||
#include "jsweakmap.h"
|
||||
#include "methodjit/MethodJIT.h"
|
||||
#include "vm/String.h"
|
||||
|
||||
#include "jsstrinlines.h"
|
||||
#include "jscompartment.h"
|
||||
#include "jsobjinlines.h"
|
||||
|
||||
#include "vm/String-inl.h"
|
||||
|
||||
#ifdef MOZ_VALGRIND
|
||||
# define JS_VALGRIND
|
||||
|
@ -166,20 +166,39 @@ const uint8 GCThingSizeMap[] = {
|
|||
|
||||
JS_STATIC_ASSERT(JS_ARRAY_LENGTH(GCThingSizeMap) == FINALIZE_LIMIT);
|
||||
|
||||
inline FreeCell *
|
||||
ArenaHeader::getFreeList() const
|
||||
#ifdef DEBUG
|
||||
void
|
||||
ArenaHeader::checkSynchronizedWithFreeList() const
|
||||
{
|
||||
/*
|
||||
* 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(compartment);
|
||||
JS_ASSERT_IF(freeList &&
|
||||
compartment->freeLists.finalizables[getThingKind()] &&
|
||||
this == compartment->freeLists.finalizables[getThingKind()]->arenaHeader(),
|
||||
freeList == compartment->freeLists.finalizables[getThingKind()]);
|
||||
return freeList;
|
||||
|
||||
/*
|
||||
* 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(address() + firstFreeSpanStart, address() + firstFreeSpanEnd);
|
||||
if (firstSpan.isEmpty())
|
||||
return;
|
||||
FreeSpan *list = &compartment->freeLists.lists[getThingKind()];
|
||||
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.start == list->start);
|
||||
JS_ASSERT(firstSpan.end == list->end);
|
||||
}
|
||||
#endif
|
||||
|
||||
template<typename T>
|
||||
inline bool
|
||||
|
@ -188,74 +207,81 @@ Arena::finalize(JSContext *cx)
|
|||
JS_ASSERT(aheader.compartment);
|
||||
JS_ASSERT(!aheader.getMarkingDelay()->link);
|
||||
|
||||
uintptr_t nextFree = reinterpret_cast<uintptr_t>(aheader.getFreeList());
|
||||
FreeCell *freeList = NULL;
|
||||
FreeCell **tailp = &freeList;
|
||||
bool allClear = true;
|
||||
|
||||
uintptr_t thing = thingsStart(sizeof(T));
|
||||
uintptr_t end = thingsEnd();
|
||||
|
||||
if (!nextFree) {
|
||||
nextFree = end;
|
||||
} else {
|
||||
JS_ASSERT(thing <= nextFree);
|
||||
JS_ASSERT(nextFree < end);
|
||||
}
|
||||
FreeSpan nextFree(aheader.getFirstFreeSpan());
|
||||
nextFree.checkSpan();
|
||||
|
||||
FreeSpan newListHead;
|
||||
FreeSpan *newListTail = &newListHead;
|
||||
uintptr_t newFreeSpanStart = 0;
|
||||
bool allClear = true;
|
||||
#ifdef DEBUG
|
||||
size_t nmarked = 0;
|
||||
#endif
|
||||
for (;; thing += sizeof(T)) {
|
||||
if (thing == nextFree) {
|
||||
if (thing == end)
|
||||
JS_ASSERT(thing <= end);
|
||||
if (thing == nextFree.start) {
|
||||
JS_ASSERT(nextFree.end <= end);
|
||||
if (nextFree.end == end)
|
||||
break;
|
||||
FreeCell *nextLink = reinterpret_cast<FreeCell *>(nextFree)->link;
|
||||
if (!nextLink) {
|
||||
nextFree = end;
|
||||
} else {
|
||||
nextFree = reinterpret_cast<uintptr_t>(nextLink);
|
||||
JS_ASSERT(thing < nextFree);
|
||||
JS_ASSERT(nextFree < end);
|
||||
}
|
||||
JS_ASSERT(Arena::isAligned(nextFree.end, sizeof(T)));
|
||||
if (!newFreeSpanStart)
|
||||
newFreeSpanStart = thing;
|
||||
thing = nextFree.end;
|
||||
nextFree = *nextFree.nextSpan();
|
||||
nextFree.checkSpan();
|
||||
} else {
|
||||
T *t = reinterpret_cast<T *>(thing);
|
||||
if (t->isMarked()) {
|
||||
allClear = false;
|
||||
continue;
|
||||
}
|
||||
t->finalize(cx);
|
||||
#ifdef DEBUG
|
||||
memset(t, JS_FREE_PATTERN, sizeof(T));
|
||||
nmarked++;
|
||||
#endif
|
||||
if (newFreeSpanStart) {
|
||||
JS_ASSERT(thing >= thingsStart(sizeof(T)) + sizeof(T));
|
||||
newListTail->start = newFreeSpanStart;
|
||||
newListTail->end = thing - sizeof(T);
|
||||
newListTail = newListTail->nextSpanUnchecked();
|
||||
newFreeSpanStart = 0;
|
||||
}
|
||||
} else {
|
||||
if (!newFreeSpanStart)
|
||||
newFreeSpanStart = thing;
|
||||
t->finalize(cx);
|
||||
#ifdef DEBUG
|
||||
memset(t, JS_FREE_PATTERN, sizeof(T));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
FreeCell *freeCell = reinterpret_cast<FreeCell *>(thing);
|
||||
*tailp = freeCell;
|
||||
tailp = &freeCell->link;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
/* Check that the free list is consistent. */
|
||||
unsigned nfree = 0;
|
||||
if (freeList) {
|
||||
JS_ASSERT(tailp != &freeList);
|
||||
FreeCell *freeCell = freeList;
|
||||
for (;;) {
|
||||
++nfree;
|
||||
if (&freeCell->link == tailp)
|
||||
break;
|
||||
JS_ASSERT(freeCell < freeCell->link);
|
||||
freeCell = freeCell->link;
|
||||
}
|
||||
}
|
||||
if (allClear) {
|
||||
JS_ASSERT(nfree == Arena::thingsPerArena(sizeof(T)));
|
||||
JS_ASSERT(freeList->address() == thingsStart(sizeof(T)));
|
||||
JS_ASSERT(tailp == &reinterpret_cast<FreeCell *>(end - sizeof(T))->link);
|
||||
} else {
|
||||
JS_ASSERT(nfree < Arena::thingsPerArena(sizeof(T)));
|
||||
JS_ASSERT(newListTail == &newListHead);
|
||||
JS_ASSERT(newFreeSpanStart == thingsStart(sizeof(T)));
|
||||
return true;
|
||||
}
|
||||
|
||||
newListTail->start = newFreeSpanStart ? newFreeSpanStart : nextFree.start;
|
||||
JS_ASSERT(Arena::isAligned(newListTail->start, sizeof(T)));
|
||||
newListTail->end = end;
|
||||
|
||||
#ifdef DEBUG
|
||||
size_t nfree = 0;
|
||||
for (FreeSpan *span = &newListHead; span != newListTail; span = span->nextSpan()) {
|
||||
span->checkSpan();
|
||||
JS_ASSERT(Arena::isAligned(span->start, sizeof(T)));
|
||||
JS_ASSERT(Arena::isAligned(span->end, sizeof(T)));
|
||||
nfree += (span->end - span->start) / sizeof(T) + 1;
|
||||
JS_ASSERT(nfree + nmarked <= thingsPerArena(sizeof(T)));
|
||||
}
|
||||
nfree += (newListTail->end - newListTail->start) / sizeof(T);
|
||||
JS_ASSERT(nfree + nmarked == thingsPerArena(sizeof(T)));
|
||||
#endif
|
||||
*tailp = NULL;
|
||||
aheader.setFreeList(freeList);
|
||||
return allClear;
|
||||
aheader.setFirstFreeSpan(&newListHead);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -305,8 +331,7 @@ Chunk::init(JSRuntime *rt)
|
|||
{
|
||||
info.runtime = rt;
|
||||
info.age = 0;
|
||||
info.emptyArenaLists.init();
|
||||
info.emptyArenaLists.cellFreeList = &arenas[0].aheader;
|
||||
info.emptyArenaListHead = &arenas[0].aheader;
|
||||
ArenaHeader *aheader = &arenas[0].aheader;
|
||||
ArenaHeader *last = &arenas[JS_ARRAY_LENGTH(arenas) - 1].aheader;
|
||||
while (aheader < last) {
|
||||
|
@ -343,50 +368,18 @@ Chunk::withinArenasRange(Cell *cell)
|
|||
return false;
|
||||
}
|
||||
|
||||
/* Turn arena cells into a free list starting from the first thing. */
|
||||
template<size_t thingSize>
|
||||
static inline FreeCell *
|
||||
BuildFreeList(ArenaHeader *aheader)
|
||||
{
|
||||
uintptr_t thing = aheader->getArena()->thingsStart(thingSize);
|
||||
uintptr_t end = aheader->getArena()->thingsEnd();
|
||||
FreeCell *first = reinterpret_cast<FreeCell *>(thing);
|
||||
FreeCell **prevp = &first->link;
|
||||
|
||||
for (thing += thingSize; thing != end; thing += thingSize) {
|
||||
JS_ASSERT(thing < end);
|
||||
FreeCell *cell = reinterpret_cast<FreeCell *>(thing);
|
||||
|
||||
/*
|
||||
* Here prevp points to the link field of the previous cell in the
|
||||
* list. Write the address of the following cell into it.
|
||||
*/
|
||||
*prevp = cell;
|
||||
prevp = &cell->link;
|
||||
}
|
||||
|
||||
*prevp = NULL;
|
||||
return first;
|
||||
}
|
||||
|
||||
template <size_t thingSize>
|
||||
ArenaHeader *
|
||||
Chunk::allocateArena(JSContext *cx, unsigned thingKind)
|
||||
{
|
||||
JSCompartment *comp = cx->compartment;
|
||||
JS_ASSERT(hasAvailableArenas());
|
||||
ArenaHeader *aheader = info.emptyArenaLists.getTypedFreeList(thingKind);
|
||||
if (!aheader) {
|
||||
aheader = info.emptyArenaLists.getOtherArena();
|
||||
aheader->setFreeList(BuildFreeList<thingSize>(aheader));
|
||||
}
|
||||
JS_ASSERT(!aheader->compartment);
|
||||
JS_ASSERT(!aheader->getMarkingDelay()->link);
|
||||
aheader->compartment = comp;
|
||||
aheader->setThingKind(thingKind);
|
||||
ArenaHeader *aheader = info.emptyArenaListHead;
|
||||
info.emptyArenaListHead = aheader->next;
|
||||
aheader->init(comp, thingKind, thingSize);
|
||||
--info.numFree;
|
||||
JSRuntime *rt = info.runtime;
|
||||
|
||||
JSRuntime *rt = info.runtime;
|
||||
JS_ATOMIC_ADD(&rt->gcBytes, ArenaSize);
|
||||
JS_ATOMIC_ADD(&comp->gcBytes, ArenaSize);
|
||||
METER(JS_ATOMIC_INCREMENT(&rt->gcStats.nallarenas));
|
||||
|
@ -420,7 +413,8 @@ Chunk::releaseArena(ArenaHeader *aheader)
|
|||
#endif
|
||||
JS_ATOMIC_ADD(&rt->gcBytes, -int32(ArenaSize));
|
||||
JS_ATOMIC_ADD(&comp->gcBytes, -int32(ArenaSize));
|
||||
info.emptyArenaLists.insert(aheader);
|
||||
aheader->next = info.emptyArenaListHead;
|
||||
info.emptyArenaListHead = aheader;
|
||||
aheader->compartment = NULL;
|
||||
++info.numFree;
|
||||
if (unused())
|
||||
|
@ -520,11 +514,12 @@ PickChunk(JSContext *cx)
|
|||
|
||||
chunk->init(rt);
|
||||
cx->compartment->chunk = chunk;
|
||||
rt->gcChunkAllocationSinceLastGC = true;
|
||||
return chunk;
|
||||
}
|
||||
|
||||
static void
|
||||
ExpireGCChunks(JSRuntime *rt)
|
||||
ExpireGCChunks(JSRuntime *rt, JSGCInvocationKind gckind)
|
||||
{
|
||||
static const size_t MaxAge = 3;
|
||||
|
||||
|
@ -536,7 +531,7 @@ ExpireGCChunks(JSRuntime *rt)
|
|||
Chunk *chunk = e.front();
|
||||
JS_ASSERT(chunk->info.runtime == rt);
|
||||
if (chunk->unused()) {
|
||||
if (chunk->info.age++ > MaxAge) {
|
||||
if (gckind == GC_SHRINK || chunk->info.age++ > MaxAge) {
|
||||
e.removeFront();
|
||||
ReleaseGCChunk(rt, chunk);
|
||||
continue;
|
||||
|
@ -620,7 +615,7 @@ js_InitGC(JSRuntime *rt, uint32 maxbytes)
|
|||
* The assigned value prevents GC from running when GC memory is too low
|
||||
* (during JS engine start).
|
||||
*/
|
||||
rt->setGCLastBytes(8192);
|
||||
rt->setGCLastBytes(8192, GC_NORMAL);
|
||||
|
||||
rt->gcJitReleaseTime = PRMJ_Now() + JIT_SCRIPT_EIGHTH_LIFETIME;
|
||||
|
||||
|
@ -631,21 +626,32 @@ js_InitGC(JSRuntime *rt, uint32 maxbytes)
|
|||
namespace js {
|
||||
|
||||
inline bool
|
||||
InFreeList(ArenaHeader *aheader, void *thing)
|
||||
InFreeList(ArenaHeader *aheader, uintptr_t addr)
|
||||
{
|
||||
for (FreeCell *cursor = aheader->getFreeList(); cursor; cursor = cursor->link) {
|
||||
JS_ASSERT(!cursor->isMarked());
|
||||
JS_ASSERT_IF(cursor->link, cursor < cursor->link);
|
||||
if (!aheader->hasFreeThings())
|
||||
return false;
|
||||
|
||||
/* If the cursor moves past the thing, it's not in the freelist. */
|
||||
if (thing < cursor)
|
||||
break;
|
||||
FreeSpan firstSpan(aheader->getFirstFreeSpan());
|
||||
|
||||
/* If we find it on the freelist, it's dead. */
|
||||
if (thing == cursor)
|
||||
for (FreeSpan *span = &firstSpan;;) {
|
||||
/* If the thing comes fore the current span, it's not free. */
|
||||
if (addr < span->start)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* 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->end)
|
||||
return true;
|
||||
|
||||
/*
|
||||
* 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();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
|
@ -662,11 +668,17 @@ MarkArenaPtrConservatively(JSTracer *trc, ArenaHeader *aheader, uintptr_t addr)
|
|||
|
||||
/* addr can point inside the thing so we must align the address. */
|
||||
uintptr_t shift = (offset - minOffset) % sizeof(T);
|
||||
T *thing = reinterpret_cast<T *>(addr - shift);
|
||||
addr -= shift;
|
||||
|
||||
if (InFreeList(aheader, thing))
|
||||
/*
|
||||
* 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, addr))
|
||||
return CGCT_NOTLIVE;
|
||||
|
||||
T *thing = reinterpret_cast<T *>(addr);
|
||||
MarkRoot(trc, thing, "machine stack");
|
||||
|
||||
#ifdef JS_DUMP_CONSERVATIVE_GC_ROOTS
|
||||
|
@ -927,8 +939,6 @@ js_FinishGC(JSRuntime *rt)
|
|||
rt->compartments.clear();
|
||||
rt->atomsCompartment = NULL;
|
||||
|
||||
rt->gcWeakMapList = NULL;
|
||||
|
||||
for (GCChunkSet::Range r(rt->gcChunkSet.all()); !r.empty(); r.popFront())
|
||||
ReleaseGCChunk(rt, r.front());
|
||||
rt->gcChunkSet.clear();
|
||||
|
@ -1083,34 +1093,38 @@ js_MapGCRoots(JSRuntime *rt, JSGCRootMapFun map, void *data)
|
|||
}
|
||||
|
||||
void
|
||||
JSRuntime::setGCLastBytes(size_t lastBytes)
|
||||
JSRuntime::setGCLastBytes(size_t lastBytes, JSGCInvocationKind gckind)
|
||||
{
|
||||
gcLastBytes = lastBytes;
|
||||
float trigger = float(Max(lastBytes, GC_ARENA_ALLOCATION_TRIGGER)) * GC_HEAP_GROWTH_FACTOR;
|
||||
|
||||
size_t base = gckind == GC_SHRINK ? lastBytes : Max(lastBytes, GC_ARENA_ALLOCATION_TRIGGER);
|
||||
float trigger = float(base) * GC_HEAP_GROWTH_FACTOR;
|
||||
gcTriggerBytes = size_t(Min(float(gcMaxBytes), trigger));
|
||||
}
|
||||
|
||||
void
|
||||
JSRuntime::reduceGCTriggerBytes(uint32 amount) {
|
||||
JS_ASSERT(amount > 0);
|
||||
JS_ASSERT((gcTriggerBytes - amount) > 0);
|
||||
JS_ASSERT(gcTriggerBytes - amount >= 0);
|
||||
if (gcTriggerBytes - amount < GC_ARENA_ALLOCATION_TRIGGER * GC_HEAP_GROWTH_FACTOR)
|
||||
return;
|
||||
gcTriggerBytes -= amount;
|
||||
}
|
||||
|
||||
void
|
||||
JSCompartment::setGCLastBytes(size_t lastBytes)
|
||||
JSCompartment::setGCLastBytes(size_t lastBytes, JSGCInvocationKind gckind)
|
||||
{
|
||||
gcLastBytes = lastBytes;
|
||||
float trigger = float(Max(lastBytes, GC_ARENA_ALLOCATION_TRIGGER)) * GC_HEAP_GROWTH_FACTOR;
|
||||
|
||||
size_t base = gckind == GC_SHRINK ? lastBytes : Max(lastBytes, GC_ARENA_ALLOCATION_TRIGGER);
|
||||
float trigger = float(base) * GC_HEAP_GROWTH_FACTOR;
|
||||
gcTriggerBytes = size_t(Min(float(rt->gcMaxBytes), trigger));
|
||||
}
|
||||
|
||||
void
|
||||
JSCompartment::reduceGCTriggerBytes(uint32 amount) {
|
||||
JS_ASSERT(amount > 0);
|
||||
JS_ASSERT((gcTriggerBytes - amount) > 0);
|
||||
JS_ASSERT(gcTriggerBytes - amount >= 0);
|
||||
if (gcTriggerBytes - amount < GC_ARENA_ALLOCATION_TRIGGER * GC_HEAP_GROWTH_FACTOR)
|
||||
return;
|
||||
gcTriggerBytes -= amount;
|
||||
|
@ -1124,7 +1138,7 @@ ArenaList::searchForFreeArena()
|
|||
{
|
||||
while (ArenaHeader *aheader = *cursor) {
|
||||
cursor = &aheader->next;
|
||||
if (aheader->hasFreeList())
|
||||
if (aheader->hasFreeThings())
|
||||
return aheader;
|
||||
}
|
||||
return NULL;
|
||||
|
@ -1246,6 +1260,7 @@ ArenaList::finalizeLater(JSContext *cx)
|
|||
cursor = &head;
|
||||
backgroundFinalizeState = BFS_RUN;
|
||||
} else {
|
||||
JS_ASSERT_IF(!head, cursor == &head);
|
||||
backgroundFinalizeState = BFS_DONE;
|
||||
finalizeNow<T>(cx);
|
||||
}
|
||||
|
@ -1381,7 +1396,7 @@ RefillTypedFreeList(JSContext *cx, unsigned thingKind)
|
|||
return NULL;
|
||||
|
||||
JSCompartment *compartment = cx->compartment;
|
||||
JS_ASSERT(!compartment->freeLists.finalizables[thingKind]);
|
||||
JS_ASSERT(compartment->freeLists.lists[thingKind].isEmpty());
|
||||
|
||||
bool canGC = IsGCAllowed(cx);
|
||||
bool runGC = canGC && JS_UNLIKELY(NeedLastDitchGC(cx));
|
||||
|
@ -1395,14 +1410,14 @@ RefillTypedFreeList(JSContext *cx, unsigned thingKind)
|
|||
* things and populate the free list. If that happens, just
|
||||
* return that list head.
|
||||
*/
|
||||
if (Cell *thing = compartment->freeLists.getNext(thingKind))
|
||||
if (Cell *thing = compartment->freeLists.getNext(thingKind, sizeof(T)))
|
||||
return thing;
|
||||
}
|
||||
ArenaHeader *aheader =
|
||||
compartment->arenas[thingKind].getArenaWithFreeList<sizeof(T)>(cx, thingKind);
|
||||
if (aheader) {
|
||||
JS_ASSERT(sizeof(T) == aheader->getThingSize());
|
||||
return compartment->freeLists.populate(aheader, thingKind);
|
||||
return compartment->freeLists.populate(aheader, thingKind, sizeof(T));
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1513,7 +1528,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
|
||||
|
@ -1810,6 +1825,14 @@ MarkContext(JSTracer *trc, JSContext *acx)
|
|||
MarkValue(trc, acx->iterValue, "iterValue");
|
||||
}
|
||||
|
||||
void
|
||||
MarkWeakReferences(GCMarker *trc)
|
||||
{
|
||||
trc->drainMarkStack();
|
||||
while (js_TraceWatchPoints(trc) || WeakMapBase::markAllIteratively(trc))
|
||||
trc->drainMarkStack();
|
||||
}
|
||||
|
||||
JS_REQUIRES_STACK void
|
||||
MarkRuntime(JSTracer *trc)
|
||||
{
|
||||
|
@ -1839,9 +1862,14 @@ MarkRuntime(JSTracer *trc)
|
|||
for (ThreadDataIter i(rt); !i.empty(); i.popFront())
|
||||
i.threadData()->mark(trc);
|
||||
|
||||
if (IS_GC_MARKING_TRACER(trc)) {
|
||||
GCMarker *gcmarker = static_cast<GCMarker *>(trc);
|
||||
MarkWeakReferences(gcmarker);
|
||||
}
|
||||
|
||||
/*
|
||||
* We mark extra roots at the last thing so it can use use additional
|
||||
* colors to implement cycle collection.
|
||||
* We mark extra roots at the end so additional colors can be used
|
||||
* to implement cycle collection.
|
||||
*/
|
||||
if (rt->gcExtraRootsTraceOp)
|
||||
rt->gcExtraRootsTraceOp(trc, rt->gcExtraRootsData);
|
||||
|
@ -1937,6 +1965,21 @@ MaybeGC(JSContext *cx)
|
|||
if (comp->gcBytes > 8192 && comp->gcBytes >= 3 * (comp->gcTriggerBytes / 4)) {
|
||||
GCREASON(MAYBEGC);
|
||||
js_GC(cx, (rt->gcMode == JSGC_MODE_COMPARTMENT) ? comp : NULL, GC_NORMAL);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* On 32 bit setting gcNextFullGCTime below is not atomic and a race condition
|
||||
* could trigger an GC. We tolerate this.
|
||||
*/
|
||||
int64 now = PRMJ_Now();
|
||||
if (rt->gcNextFullGCTime && rt->gcNextFullGCTime <= now) {
|
||||
if (rt->gcChunkAllocationSinceLastGC || rt->gcChunksWaitingToExpire) {
|
||||
GCREASON(MAYBEGC);
|
||||
js_GC(cx, NULL, GC_SHRINK);
|
||||
} else {
|
||||
rt->gcNextFullGCTime = now + GC_IDLE_FULL_SPAN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2071,10 +2114,11 @@ GCHelperThread::threadLoop(JSRuntime *rt)
|
|||
}
|
||||
|
||||
void
|
||||
GCHelperThread::startBackgroundSweep(JSRuntime *rt)
|
||||
GCHelperThread::startBackgroundSweep(JSRuntime *rt, JSGCInvocationKind gckind)
|
||||
{
|
||||
/* The caller takes the GC lock. */
|
||||
JS_ASSERT(!sweeping);
|
||||
lastGCKind = gckind;
|
||||
sweeping = true;
|
||||
PR_NotifyCondVar(wakeup);
|
||||
}
|
||||
|
@ -2115,7 +2159,7 @@ GCHelperThread::doSweep()
|
|||
for (ArenaHeader **i = finalizeVector.begin(); i != finalizeVector.end(); ++i)
|
||||
ArenaList::backgroundFinalize(cx, *i);
|
||||
finalizeVector.resize(0);
|
||||
ExpireGCChunks(cx->runtime);
|
||||
ExpireGCChunks(cx->runtime, lastGCKind);
|
||||
cx = NULL;
|
||||
|
||||
if (freeCursor) {
|
||||
|
@ -2186,7 +2230,7 @@ SweepCompartments(JSContext *cx, JSGCInvocationKind gckind)
|
|||
if (!compartment->hold &&
|
||||
(compartment->arenaListsAreEmpty() || gckind == GC_LAST_CONTEXT))
|
||||
{
|
||||
JS_ASSERT(compartment->freeLists.isEmpty());
|
||||
compartment->freeLists.checkEmpty();
|
||||
if (callback)
|
||||
JS_ALWAYS_TRUE(callback(cx, compartment, JSCOMPARTMENT_DESTROY));
|
||||
if (compartment->principals)
|
||||
|
@ -2281,15 +2325,6 @@ MarkAndSweep(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIM
|
|||
|
||||
gcmarker.drainMarkStack();
|
||||
|
||||
/*
|
||||
* Mark weak roots.
|
||||
*/
|
||||
while (true) {
|
||||
if (!js_TraceWatchPoints(&gcmarker) && !WeakMap::markIteratively(&gcmarker))
|
||||
break;
|
||||
gcmarker.drainMarkStack();
|
||||
}
|
||||
|
||||
rt->gcMarkingTracer = NULL;
|
||||
|
||||
if (rt->gcCallback)
|
||||
|
@ -2320,7 +2355,7 @@ MarkAndSweep(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIM
|
|||
GCTIMESTAMP(startSweep);
|
||||
|
||||
/* Finalize unreachable (key,value) pairs in all weak maps. */
|
||||
WeakMap::sweep(cx);
|
||||
WeakMapBase::sweepAll(&gcmarker);
|
||||
|
||||
js_SweepAtomState(cx);
|
||||
|
||||
|
@ -2391,7 +2426,7 @@ MarkAndSweep(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIM
|
|||
* use IsAboutToBeFinalized().
|
||||
* This is done on the GCHelperThread if JS_THREADSAFE is defined.
|
||||
*/
|
||||
ExpireGCChunks(rt);
|
||||
ExpireGCChunks(rt, gckind);
|
||||
#endif
|
||||
GCTIMESTAMP(sweepDestroyEnd);
|
||||
|
||||
|
@ -2647,7 +2682,7 @@ GCCycle(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIMER_P
|
|||
if (gckind != GC_LAST_CONTEXT && rt->state != JSRTS_LANDING) {
|
||||
JS_ASSERT(cx->gcBackgroundFree == &rt->gcHelperThread);
|
||||
cx->gcBackgroundFree = NULL;
|
||||
rt->gcHelperThread.startBackgroundSweep(rt);
|
||||
rt->gcHelperThread.startBackgroundSweep(rt, gckind);
|
||||
} else {
|
||||
JS_ASSERT(!cx->gcBackgroundFree);
|
||||
}
|
||||
|
@ -2655,11 +2690,12 @@ GCCycle(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIMER_P
|
|||
|
||||
rt->gcMarkAndSweep = false;
|
||||
rt->gcRegenShapes = false;
|
||||
rt->setGCLastBytes(rt->gcBytes);
|
||||
rt->setGCLastBytes(rt->gcBytes, gckind);
|
||||
rt->gcCurrentCompartment = NULL;
|
||||
rt->gcWeakMapList = NULL;
|
||||
|
||||
for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c)
|
||||
(*c)->setGCLastBytes((*c)->gcBytes);
|
||||
(*c)->setGCLastBytes((*c)->gcBytes, gckind);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -2716,6 +2752,10 @@ js_GC(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind)
|
|||
* stop creating garbage.
|
||||
*/
|
||||
} while (gckind == GC_LAST_CONTEXT && rt->gcPoke);
|
||||
|
||||
rt->gcNextFullGCTime = PRMJ_Now() + GC_IDLE_FULL_SPAN;
|
||||
|
||||
rt->gcChunkAllocationSinceLastGC = false;
|
||||
#ifdef JS_GCMETER
|
||||
js_DumpGCStats(cx->runtime, stderr);
|
||||
#endif
|
||||
|
@ -2787,14 +2827,18 @@ IterateCompartmentCells(JSContext *cx, JSCompartment *comp, uint64 traceKindMask
|
|||
ArenaHeader *aheader = comp->arenas[thingKind].getHead();
|
||||
for (; aheader; aheader = aheader->next) {
|
||||
Arena *a = aheader->getArena();
|
||||
uintptr_t end = a->thingsEnd();
|
||||
FreeCell *nextFree = aheader->getFreeList();
|
||||
for (uintptr_t thing = a->thingsStart(thingSize); thing != end; thing += thingSize) {
|
||||
Cell *t = reinterpret_cast<Cell *>(thing);
|
||||
if (t == nextFree)
|
||||
nextFree = nextFree->link;
|
||||
else
|
||||
(*callback)(cx, data, traceKind, t);
|
||||
FreeSpan firstSpan(aheader->getFirstFreeSpan());
|
||||
FreeSpan *span = &firstSpan;
|
||||
for (uintptr_t thing = a->thingsStart(thingSize);; thing += thingSize) {
|
||||
JS_ASSERT(thing <= a->thingsEnd());
|
||||
if (thing == span->start) {
|
||||
if (!span->hasNext())
|
||||
break;
|
||||
thing = span->end;
|
||||
span = span->nextSpan();
|
||||
} else {
|
||||
(*callback)(cx, data, traceKind, reinterpret_cast<void *>(thing));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2838,7 +2882,7 @@ NewCompartment(JSContext *cx, JSPrincipals *principals)
|
|||
JSPRINCIPALS_HOLD(cx, principals);
|
||||
}
|
||||
|
||||
compartment->setGCLastBytes(8192);
|
||||
compartment->setGCLastBytes(8192, GC_NORMAL);
|
||||
|
||||
/*
|
||||
* Before reporting the OOM condition, |lock| needs to be cleaned up,
|
||||
|
|
364
js/src/jsgc.h
364
js/src/jsgc.h
|
@ -124,17 +124,101 @@ 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.
|
||||
* |start| is the address of the first free cell in the span. |end| is the
|
||||
* address of the last free cell in the span. The last cell (starting at
|
||||
* |end|) holds a FreeSpan data structure for the next span. However, the last
|
||||
* FreeSpan in an Arena is special: |end| points to the end of the Arena (an
|
||||
* unusable address), and no next FreeSpan is stored there.
|
||||
*
|
||||
* As things in the arena ends on its boundary that is aligned on ArenaSize,
|
||||
* end & ArenaMask is zero if and only if the span is last. Also, since the
|
||||
* first thing in the arena comes after the header, start & ArenaSize is zero
|
||||
* if and only if the span is the empty span at the end of the arena.
|
||||
*
|
||||
* The type of the start and end fields is uintptr_t, not a pointer type, to
|
||||
* minimize the amount of casting when doing mask operations.
|
||||
*/
|
||||
struct FreeSpan {
|
||||
uintptr_t start;
|
||||
uintptr_t end;
|
||||
|
||||
struct FreeCell : Cell {
|
||||
FreeCell *link;
|
||||
public:
|
||||
FreeSpan() { }
|
||||
|
||||
FreeSpan(uintptr_t start, uintptr_t end)
|
||||
: start(start), end(end) {
|
||||
checkSpan();
|
||||
}
|
||||
|
||||
bool isEmpty() const {
|
||||
checkSpan();
|
||||
return !(start & ArenaMask);
|
||||
}
|
||||
|
||||
bool hasNext() const {
|
||||
checkSpan();
|
||||
return !!(end & ArenaMask);
|
||||
}
|
||||
|
||||
FreeSpan *nextSpan() const {
|
||||
JS_ASSERT(hasNext());
|
||||
return reinterpret_cast<FreeSpan *>(end);
|
||||
}
|
||||
|
||||
FreeSpan *nextSpanUnchecked() const {
|
||||
JS_ASSERT(end & ArenaMask);
|
||||
return reinterpret_cast<FreeSpan *>(end);
|
||||
}
|
||||
|
||||
uintptr_t arenaAddress() const {
|
||||
JS_ASSERT(!isEmpty());
|
||||
return start & ~ArenaMask;
|
||||
}
|
||||
|
||||
void checkSpan() const {
|
||||
#ifdef DEBUG
|
||||
JS_ASSERT(start <= end);
|
||||
JS_ASSERT(end - start <= ArenaSize);
|
||||
if (!(start & ArenaMask)) {
|
||||
/* The span is last and empty. */
|
||||
JS_ASSERT(start == end);
|
||||
return;
|
||||
}
|
||||
|
||||
JS_ASSERT(start);
|
||||
JS_ASSERT(end);
|
||||
uintptr_t arena = start & ~ArenaMask;
|
||||
if (!(end & ArenaMask)) {
|
||||
/* The last span with few free things at the end of the arena. */
|
||||
JS_ASSERT(arena + ArenaSize == end);
|
||||
return;
|
||||
}
|
||||
|
||||
/* The span is not last and we have at least one span that follows it.*/
|
||||
JS_ASSERT(arena == (end & ~ArenaMask));
|
||||
FreeSpan *next = reinterpret_cast<FreeSpan *>(end);
|
||||
|
||||
void checkLink() {
|
||||
/*
|
||||
* The GC things on the free lists come from one arena and the things
|
||||
* on the free list are linked in ascending address order.
|
||||
* 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_IF(link, arenaHeader() == link->arenaHeader());
|
||||
JS_ASSERT_IF(link, this < link);
|
||||
JS_ASSERT(end < next->start);
|
||||
|
||||
if (!(next->start & ArenaMask)) {
|
||||
/*
|
||||
* 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->start == next->end);
|
||||
JS_ASSERT(arena + ArenaSize == next->start);
|
||||
} else {
|
||||
/* The next spans is not empty and must starts inside the arena. */
|
||||
JS_ASSERT(arena == (next->start & ~ArenaMask));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -144,7 +228,16 @@ struct ArenaHeader {
|
|||
ArenaHeader *next;
|
||||
|
||||
private:
|
||||
FreeCell *freeList;
|
||||
/*
|
||||
* 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. When the arena has no free things, the span
|
||||
* must be the empty one pointing to the arena's end. For such a span the
|
||||
* start and end offsets must be ArenaSize.
|
||||
*/
|
||||
uint16_t firstFreeSpanStart;
|
||||
uint16_t firstFreeSpanEnd;
|
||||
|
||||
unsigned thingKind;
|
||||
|
||||
friend struct FreeLists;
|
||||
|
@ -153,31 +246,37 @@ struct ArenaHeader {
|
|||
inline uintptr_t address() const;
|
||||
inline Chunk *chunk() const;
|
||||
|
||||
inline void init(JSCompartment *comp, unsigned thingKind, size_t thingSize);
|
||||
|
||||
Arena *getArena() {
|
||||
return reinterpret_cast<Arena *>(address());
|
||||
}
|
||||
|
||||
bool hasFreeList() const {
|
||||
return !!freeList;
|
||||
}
|
||||
|
||||
inline FreeCell *getFreeList() const;
|
||||
|
||||
void setFreeList(FreeCell *head) {
|
||||
JS_ASSERT_IF(head, head->arenaHeader() == this);
|
||||
freeList = head;
|
||||
}
|
||||
|
||||
void clearFreeList() {
|
||||
freeList = NULL;
|
||||
}
|
||||
|
||||
unsigned getThingKind() const {
|
||||
return thingKind;
|
||||
}
|
||||
|
||||
void setThingKind(unsigned kind) {
|
||||
thingKind = kind;
|
||||
bool hasFreeThings() const {
|
||||
return firstFreeSpanStart != ArenaSize;
|
||||
}
|
||||
|
||||
void setAsFullyUsed() {
|
||||
firstFreeSpanStart = firstFreeSpanEnd = uint16_t(ArenaSize);
|
||||
}
|
||||
|
||||
FreeSpan getFirstFreeSpan() const {
|
||||
#ifdef DEBUG
|
||||
checkSynchronizedWithFreeList();
|
||||
#endif
|
||||
return FreeSpan(address() + firstFreeSpanStart, address() + firstFreeSpanEnd);
|
||||
}
|
||||
|
||||
void setFirstFreeSpan(const FreeSpan *span) {
|
||||
span->checkSpan();
|
||||
JS_ASSERT(span->start - address() <= ArenaSize);
|
||||
JS_ASSERT(span->end - address() <= ArenaSize);
|
||||
firstFreeSpanStart = uint16_t(span->start - address());
|
||||
firstFreeSpanEnd = uint16_t(span->end - address());
|
||||
}
|
||||
|
||||
inline MarkingDelay *getMarkingDelay() const;
|
||||
|
@ -186,6 +285,10 @@ struct ArenaHeader {
|
|||
return GCThingSizeMap[getThingKind()];
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
void checkSynchronizedWithFreeList() const;
|
||||
#endif
|
||||
|
||||
#if defined DEBUG || defined JS_GCMETER
|
||||
static size_t CountListLength(const ArenaHeader *aheader) {
|
||||
size_t n = 0;
|
||||
|
@ -220,11 +323,28 @@ struct Arena {
|
|||
|
||||
static size_t thingsPerArena(size_t thingSize) {
|
||||
JS_ASSERT(thingSize % Cell::CellSize == 0);
|
||||
|
||||
/* We should be able to fit FreeSpan in any GC thing. */
|
||||
JS_ASSERT(thingSize >= sizeof(FreeSpan));
|
||||
|
||||
/* GCThingSizeMap assumes that any thing fits uint8. */
|
||||
JS_ASSERT(thingSize < 256);
|
||||
|
||||
return (ArenaSize - sizeof(ArenaHeader)) / thingSize;
|
||||
}
|
||||
|
||||
static size_t thingsSpan(size_t thingSize) {
|
||||
return thingsPerArena(thingSize) * thingSize;
|
||||
}
|
||||
|
||||
static size_t thingsStartOffset(size_t thingSize) {
|
||||
return ArenaSize - thingsPerArena(thingSize) * thingSize;
|
||||
return ArenaSize - thingsSpan(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 {
|
||||
|
@ -266,54 +386,11 @@ struct MarkingDelay {
|
|||
}
|
||||
};
|
||||
|
||||
struct EmptyArenaLists {
|
||||
/* Arenas with no internal freelist prepared. */
|
||||
ArenaHeader *cellFreeList;
|
||||
|
||||
/* Arenas with internal freelists prepared for a given finalize kind. */
|
||||
ArenaHeader *freeLists[FINALIZE_LIMIT];
|
||||
|
||||
void init() {
|
||||
PodZero(this);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
ArenaHeader *getTypedFreeList(unsigned thingKind) {
|
||||
JS_ASSERT(thingKind < FINALIZE_LIMIT);
|
||||
ArenaHeader *aheader = freeLists[thingKind];
|
||||
if (aheader)
|
||||
freeLists[thingKind] = aheader->next;
|
||||
return aheader;
|
||||
}
|
||||
|
||||
void insert(ArenaHeader *aheader) {
|
||||
unsigned thingKind = aheader->getThingKind();
|
||||
aheader->next = freeLists[thingKind];
|
||||
freeLists[thingKind] = aheader;
|
||||
}
|
||||
};
|
||||
|
||||
/* The chunk header (located at the end of the chunk to preserve arena alignment). */
|
||||
struct ChunkInfo {
|
||||
Chunk *link;
|
||||
JSRuntime *runtime;
|
||||
EmptyArenaLists emptyArenaLists;
|
||||
ArenaHeader *emptyArenaListHead;
|
||||
size_t age;
|
||||
size_t numFree;
|
||||
};
|
||||
|
@ -455,12 +532,21 @@ Cell::chunk() const
|
|||
inline bool
|
||||
Cell::isAligned() const
|
||||
{
|
||||
/* Things ends at the arena end. */
|
||||
uintptr_t offset = ArenaSize - (address() & ArenaMask);
|
||||
return offset % arenaHeader()->getThingSize() == 0;
|
||||
return Arena::isAligned(address(), arenaHeader()->getThingSize());
|
||||
}
|
||||
#endif
|
||||
|
||||
inline void
|
||||
ArenaHeader::init(JSCompartment *comp, unsigned kind, size_t thingSize)
|
||||
{
|
||||
JS_ASSERT(!compartment);
|
||||
JS_ASSERT(!getMarkingDelay()->link);
|
||||
compartment = comp;
|
||||
thingKind = kind;
|
||||
firstFreeSpanStart = uint16_t(Arena::thingsStartOffset(thingSize));
|
||||
firstFreeSpanEnd = uint16_t(ArenaSize);
|
||||
}
|
||||
|
||||
inline uintptr_t
|
||||
ArenaHeader::address() const
|
||||
{
|
||||
|
@ -550,6 +636,9 @@ const size_t GC_ARENA_ALLOCATION_TRIGGER = 30 * js::GC_CHUNK_SIZE;
|
|||
*/
|
||||
const float GC_HEAP_GROWTH_FACTOR = 3.0f;
|
||||
|
||||
/* Perform a Full GC every 20 seconds if MaybeGC is called */
|
||||
static const int64 GC_IDLE_FULL_SPAN = 20 * 1000 * 1000;
|
||||
|
||||
static inline size_t
|
||||
GetFinalizableTraceKind(size_t thingKind)
|
||||
{
|
||||
|
@ -703,23 +792,22 @@ class ArenaList {
|
|||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* For a given arena, finalizables[thingKind] points to the next object to be
|
||||
* allocated. It gets initialized, in RefillTypedFreeList, to the first free
|
||||
* cell in an arena. For each allocation, it is advanced to the next free cell
|
||||
* in the same arena. While finalizables[thingKind] points to a cell in an
|
||||
* arena, that arena's freeList pointer is NULL. Before doing a GC, we copy
|
||||
* finalizables[thingKind] back to the arena header's freeList pointer and set
|
||||
* finalizables[thingKind] to NULL. Thus, we only have to maintain one free
|
||||
* list pointer at any time and avoid accessing and updating the arena header
|
||||
* on each allocation.
|
||||
*/
|
||||
struct FreeLists {
|
||||
FreeCell *finalizables[FINALIZE_LIMIT];
|
||||
/*
|
||||
* For each arena kind its free list is represented as the first span with
|
||||
* free things. Initially all the spans are zeroed to be treated as empty
|
||||
* spans by the allocation code. After we find a new arena with available
|
||||
* things we copy its first free span into the list and set the arena as
|
||||
* if it has no free things. This 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 lists[FINALIZE_LIMIT];
|
||||
|
||||
void init() {
|
||||
for (size_t i = 0; i < JS_ARRAY_LENGTH(finalizables); i++)
|
||||
finalizables[i] = NULL;
|
||||
for (size_t i = 0; i != JS_ARRAY_LENGTH(lists); ++i)
|
||||
lists[i].start = lists[i].end = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -727,11 +815,13 @@ struct FreeLists {
|
|||
* run the finalizers over unitialized bytes from free things.
|
||||
*/
|
||||
void purge() {
|
||||
for (FreeCell **p = finalizables; p != JS_ARRAY_END(finalizables); ++p) {
|
||||
if (FreeCell *head = *p) {
|
||||
JS_ASSERT(!head->arenaHeader()->freeList);
|
||||
head->arenaHeader()->freeList = head;
|
||||
*p = NULL;
|
||||
for (size_t i = 0; i != size_t(FINALIZE_LIMIT); ++i) {
|
||||
FreeSpan *list = &lists[i];
|
||||
if (!list->isEmpty()) {
|
||||
ArenaHeader *aheader = reinterpret_cast<Cell *>(list->start)->arenaHeader();
|
||||
JS_ASSERT(!aheader->hasFreeThings());
|
||||
aheader->setFirstFreeSpan(list);
|
||||
list->start = list->end = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -742,10 +832,12 @@ struct FreeLists {
|
|||
* outside the GC.
|
||||
*/
|
||||
void copyToArenas() {
|
||||
for (FreeCell **p = finalizables; p != JS_ARRAY_END(finalizables); ++p) {
|
||||
if (FreeCell *head = *p) {
|
||||
JS_ASSERT(!head->arenaHeader()->freeList);
|
||||
head->arenaHeader()->freeList = head;
|
||||
for (size_t i = 0; i != size_t(FINALIZE_LIMIT); ++i) {
|
||||
FreeSpan *list = &lists[i];
|
||||
if (!list->isEmpty()) {
|
||||
ArenaHeader *aheader = reinterpret_cast<Cell *>(list->start)->arenaHeader();
|
||||
JS_ASSERT(!aheader->hasFreeThings());
|
||||
aheader->setFirstFreeSpan(list);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -755,41 +847,60 @@ struct FreeLists {
|
|||
* copyToArenas.
|
||||
*/
|
||||
void clearInArenas() {
|
||||
for (FreeCell **p = finalizables; p != JS_ARRAY_END(finalizables); ++p) {
|
||||
if (FreeCell *head = *p) {
|
||||
JS_ASSERT(head->arenaHeader()->freeList == head);
|
||||
head->arenaHeader()->clearFreeList();
|
||||
for (size_t i = 0; i != size_t(FINALIZE_LIMIT); ++i) {
|
||||
FreeSpan *list = &lists[i];
|
||||
if (!list->isEmpty()) {
|
||||
ArenaHeader *aheader = reinterpret_cast<Cell *>(list->start)->arenaHeader();
|
||||
#ifdef DEBUG
|
||||
FreeSpan span(aheader->getFirstFreeSpan());
|
||||
JS_ASSERT(span.start == list->start);
|
||||
JS_ASSERT(span.end == list->end);
|
||||
#endif
|
||||
aheader->setAsFullyUsed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FreeCell *getNext(unsigned kind) {
|
||||
FreeCell *top = finalizables[kind];
|
||||
if (top) {
|
||||
top->checkLink();
|
||||
finalizables[kind] = top->link;
|
||||
JS_ALWAYS_INLINE Cell *getNext(unsigned thingKind, size_t thingSize) {
|
||||
FreeSpan *list = &lists[thingKind];
|
||||
list->checkSpan();
|
||||
uintptr_t thing = list->start;
|
||||
if (thing != list->end) {
|
||||
/*
|
||||
* We either have at least one thing in the span that ends the
|
||||
* arena list or we have at least two things in the non-last span.
|
||||
* In both cases we just need to bump the start pointer to account
|
||||
* for the allocation.
|
||||
*/
|
||||
list->start += thingSize;
|
||||
JS_ASSERT(list->start <= list->end);
|
||||
} else if (thing & ArenaMask) {
|
||||
/*
|
||||
* The thing points to the last thing in the span that has at
|
||||
* least one more span to follow. Return the thing and update
|
||||
* the list with that next span.
|
||||
*/
|
||||
*list = *list->nextSpan();
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
return top;
|
||||
return reinterpret_cast<Cell *>(thing);
|
||||
}
|
||||
|
||||
Cell *populate(ArenaHeader *aheader, uint32 thingKind) {
|
||||
JS_ASSERT(!finalizables[thingKind]);
|
||||
FreeCell *cell = aheader->freeList;
|
||||
cell->checkLink();
|
||||
aheader->freeList = NULL;
|
||||
finalizables[thingKind] = cell->link;
|
||||
return cell;
|
||||
Cell *populate(ArenaHeader *aheader, unsigned thingKind, size_t thingSize) {
|
||||
lists[thingKind] = aheader->getFirstFreeSpan();
|
||||
aheader->setAsFullyUsed();
|
||||
Cell *t = getNext(thingKind, thingSize);
|
||||
JS_ASSERT(t);
|
||||
return t;
|
||||
}
|
||||
|
||||
void checkEmpty() {
|
||||
#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 != JS_ARRAY_LENGTH(lists); ++i)
|
||||
JS_ASSERT(lists[i].isEmpty());
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
extern Cell *
|
||||
|
@ -940,7 +1051,10 @@ typedef enum JSGCInvocationKind {
|
|||
* 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
|
||||
GC_LAST_CONTEXT = 1,
|
||||
|
||||
/* Minimize GC triggers and release empty GC chunks right away. */
|
||||
GC_SHRINK = 2
|
||||
} JSGCInvocationKind;
|
||||
|
||||
/* Pass NULL for |comp| to get a full GC. */
|
||||
|
@ -990,6 +1104,7 @@ class GCHelperThread {
|
|||
PRCondVar* wakeup;
|
||||
PRCondVar* sweepingDone;
|
||||
bool shutdown;
|
||||
JSGCInvocationKind lastGCKind;
|
||||
|
||||
Vector<void **, 16, js::SystemAllocPolicy> freeVector;
|
||||
void **freeCursor;
|
||||
|
@ -1029,7 +1144,7 @@ class GCHelperThread {
|
|||
void finish(JSRuntime *rt);
|
||||
|
||||
/* Must be called with GC lock taken. */
|
||||
void startBackgroundSweep(JSRuntime *rt);
|
||||
void startBackgroundSweep(JSRuntime *rt, JSGCInvocationKind gckind);
|
||||
|
||||
void waitBackgroundSweepEnd(JSRuntime *rt, bool gcUnlocked = true);
|
||||
|
||||
|
@ -1234,6 +1349,9 @@ struct GCMarker : public JSTracer {
|
|||
}
|
||||
};
|
||||
|
||||
JS_FRIEND_API(void)
|
||||
MarkWeakReferences(GCMarker *trc);
|
||||
|
||||
void
|
||||
MarkStackRangeConservatively(JSTracer *trc, Value *begin, Value *end);
|
||||
|
||||
|
@ -1258,7 +1376,7 @@ typedef void (*IterateCallback)(JSContext *cx, void *data, size_t traceKind, voi
|
|||
* selected. The mask should be constructed by ORing |TraceKindMask(...)|
|
||||
* results.
|
||||
*/
|
||||
void
|
||||
extern JS_FRIEND_API(void)
|
||||
IterateCells(JSContext *cx, JSCompartment *comp, uint64 traceKindMask,
|
||||
void *data, IterateCallback callback);
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
#include "jscntxt.h"
|
||||
#include "jscompartment.h"
|
||||
#include "jsscope.h"
|
||||
#include "jsxml.h"
|
||||
|
||||
#include "jslock.h"
|
||||
#include "jstl.h"
|
||||
|
@ -194,9 +195,10 @@ GCPoke(JSContext *cx, Value oldval)
|
|||
|
||||
template <typename T>
|
||||
inline T *
|
||||
NewFinalizableGCThing(JSContext *cx, unsigned thingKind)
|
||||
NewGCThing(JSContext *cx, unsigned thingKind, size_t thingSize)
|
||||
{
|
||||
JS_ASSERT(thingKind < js::gc::FINALIZE_LIMIT);
|
||||
JS_ASSERT(thingSize == js::gc::GCThingSizeMap[thingKind]);
|
||||
#ifdef JS_THREADSAFE
|
||||
JS_ASSERT_IF((cx->compartment == cx->runtime->atomsCompartment),
|
||||
(thingKind == js::gc::FINALIZE_STRING) ||
|
||||
|
@ -210,7 +212,7 @@ NewFinalizableGCThing(JSContext *cx, unsigned thingKind)
|
|||
#endif
|
||||
|
||||
METER(cx->compartment->arenas[thingKind].stats.alloc++);
|
||||
js::gc::Cell *cell = cx->compartment->freeLists.getNext(thingKind);
|
||||
js::gc::Cell *cell = cx->compartment->freeLists.getNext(thingKind, thingSize);
|
||||
return static_cast<T *>(cell ? cell : js::gc::RefillFinalizableFreeList(cx, thingKind));
|
||||
}
|
||||
|
||||
|
@ -221,7 +223,7 @@ inline JSObject *
|
|||
js_NewGCObject(JSContext *cx, js::gc::FinalizeKind kind)
|
||||
{
|
||||
JS_ASSERT(kind >= js::gc::FINALIZE_OBJECT0 && kind <= js::gc::FINALIZE_OBJECT_LAST);
|
||||
JSObject *obj = NewFinalizableGCThing<JSObject>(cx, kind);
|
||||
JSObject *obj = NewGCThing<JSObject>(cx, kind, js::gc::GCThingSizeMap[kind]);
|
||||
if (obj) {
|
||||
obj->capacity = js::gc::GetGCKindSlots(kind);
|
||||
obj->lastProp = NULL; /* Stops obj from being scanned until initializated. */
|
||||
|
@ -232,27 +234,26 @@ js_NewGCObject(JSContext *cx, js::gc::FinalizeKind kind)
|
|||
inline JSString *
|
||||
js_NewGCString(JSContext *cx)
|
||||
{
|
||||
return NewFinalizableGCThing<JSString>(cx, js::gc::FINALIZE_STRING);
|
||||
return NewGCThing<JSString>(cx, js::gc::FINALIZE_STRING, sizeof(JSString));
|
||||
}
|
||||
|
||||
inline JSShortString *
|
||||
js_NewGCShortString(JSContext *cx)
|
||||
{
|
||||
return NewFinalizableGCThing<JSShortString>(cx, js::gc::FINALIZE_SHORT_STRING);
|
||||
return NewGCThing<JSShortString>(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<JSExternalString>(cx, js::gc::FINALIZE_EXTERNAL_STRING);
|
||||
return str;
|
||||
return NewGCThing<JSExternalString>(cx, js::gc::FINALIZE_EXTERNAL_STRING,
|
||||
sizeof(JSExternalString));
|
||||
}
|
||||
|
||||
inline JSFunction*
|
||||
js_NewGCFunction(JSContext *cx)
|
||||
{
|
||||
JSFunction *fun = NewFinalizableGCThing<JSFunction>(cx, js::gc::FINALIZE_FUNCTION);
|
||||
JSFunction *fun = NewGCThing<JSFunction>(cx, js::gc::FINALIZE_FUNCTION, sizeof(JSFunction));
|
||||
if (fun) {
|
||||
fun->capacity = JSObject::FUN_CLASS_RESERVED_SLOTS;
|
||||
fun->lastProp = NULL; /* Stops fun from being scanned until initializated. */
|
||||
|
@ -263,14 +264,14 @@ js_NewGCFunction(JSContext *cx)
|
|||
inline js::Shape *
|
||||
js_NewGCShape(JSContext *cx)
|
||||
{
|
||||
return NewFinalizableGCThing<js::Shape>(cx, js::gc::FINALIZE_SHAPE);
|
||||
return NewGCThing<js::Shape>(cx, js::gc::FINALIZE_SHAPE, sizeof(js::Shape));
|
||||
}
|
||||
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
inline JSXML *
|
||||
js_NewGCXML(JSContext *cx)
|
||||
{
|
||||
return NewFinalizableGCThing<JSXML>(cx, js::gc::FINALIZE_XML);
|
||||
return NewGCThing<JSXML>(cx, js::gc::FINALIZE_XML, sizeof(JSXML));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -45,6 +45,8 @@
|
|||
#include "jsobjinlines.h"
|
||||
#include "jsscopeinlines.h"
|
||||
|
||||
#include "vm/String-inl.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
|
||||
|
|
|
@ -808,7 +808,7 @@ struct DefaultHasher<T *>: PointerHasher<T *, tl::FloorLog2<sizeof(void *)>::res
|
|||
* HashPolicy requirements:
|
||||
* - see "Hash policy" above (default js::DefaultHasher<Key>)
|
||||
* AllocPolicy:
|
||||
* - see "Allocation policies" in jsalloc.h (default js::ContextAllocPolicy)
|
||||
* - 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.
|
||||
|
@ -1041,7 +1041,7 @@ class HashMap
|
|||
* HashPolicy requirements:
|
||||
* - see "Hash policy" above (default js::DefaultHasher<Key>)
|
||||
* AllocPolicy:
|
||||
* - see "Allocation policies" in jsalloc.h (default js::ContextAllocPolicy)
|
||||
* - 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.
|
||||
|
|
|
@ -87,10 +87,10 @@
|
|||
#include "jspropertycacheinlines.h"
|
||||
#include "jsscopeinlines.h"
|
||||
#include "jsscriptinlines.h"
|
||||
#include "jsstrinlines.h"
|
||||
#include "jsopcodeinlines.h"
|
||||
|
||||
#include "vm/Stack-inl.h"
|
||||
#include "vm/String-inl.h"
|
||||
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
#include "jsxml.h"
|
||||
|
@ -1079,10 +1079,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()) {
|
||||
|
@ -1889,15 +1888,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)
|
||||
|
@ -2085,7 +2075,7 @@ static inline bool
|
|||
IteratorMore(JSContext *cx, JSObject *iterobj, bool *cond, Value *rval)
|
||||
{
|
||||
if (iterobj->getClass() == &js_IteratorClass) {
|
||||
NativeIterator *ni = (NativeIterator *) iterobj->getPrivate();
|
||||
NativeIterator *ni = iterobj->getNativeIterator();
|
||||
if (ni->isKeyIter()) {
|
||||
*cond = (ni->props_cursor < ni->props_end);
|
||||
return true;
|
||||
|
@ -2101,7 +2091,7 @@ static inline bool
|
|||
IteratorNext(JSContext *cx, JSObject *iterobj, Value *rval)
|
||||
{
|
||||
if (iterobj->getClass() == &js_IteratorClass) {
|
||||
NativeIterator *ni = (NativeIterator *) iterobj->getPrivate();
|
||||
NativeIterator *ni = iterobj->getNativeIterator();
|
||||
if (ni->isKeyIter()) {
|
||||
JS_ASSERT(ni->props_cursor < ni->props_end);
|
||||
jsid id = *ni->current();
|
||||
|
@ -3427,17 +3417,17 @@ END_CASE(JSOP_CASEX)
|
|||
|
||||
#define RELATIONAL_OP(OP) \
|
||||
JS_BEGIN_MACRO \
|
||||
Value rval = regs.sp[-1]; \
|
||||
Value lval = regs.sp[-2]; \
|
||||
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 (!ToPrimitive(cx, JSTYPE_NUMBER, &lval)) \
|
||||
goto error; \
|
||||
if (!ToPrimitive(cx, JSTYPE_NUMBER, &rval)) \
|
||||
goto error; \
|
||||
if (lval.isString() && rval.isString()) { \
|
||||
JSString *l = lval.toString(), *r = rval.toString(); \
|
||||
int32 result; \
|
||||
|
@ -3454,8 +3444,8 @@ END_CASE(JSOP_CASEX)
|
|||
} \
|
||||
} \
|
||||
TRY_BRANCH_AFTER_COND(cond, 2); \
|
||||
regs.sp[-2].setBoolean(cond); \
|
||||
regs.sp--; \
|
||||
regs.sp[-1].setBoolean(cond); \
|
||||
JS_END_MACRO
|
||||
|
||||
BEGIN_CASE(JSOP_LT)
|
||||
|
@ -3516,31 +3506,31 @@ END_CASE(JSOP_URSH)
|
|||
|
||||
BEGIN_CASE(JSOP_ADD)
|
||||
{
|
||||
Value rval = regs.sp[-1];
|
||||
Value lval = regs.sp[-2];
|
||||
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));
|
||||
regs.sp[-2].setDouble(double(l) + double(r));
|
||||
else
|
||||
regs.sp[-1].setInt32(sum);
|
||||
regs.sp[-2].setInt32(sum);
|
||||
regs.sp--;
|
||||
} else
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
if (IsXML(lval) && IsXML(rval)) {
|
||||
if (!js_ConcatenateXML(cx, &lval.toObject(), &rval.toObject(), &rval))
|
||||
goto error;
|
||||
regs.sp[-2] = rval;
|
||||
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);
|
||||
if (!ToPrimitive(cx, &lval))
|
||||
goto error;
|
||||
if (!ToPrimitive(cx, &rval))
|
||||
goto error;
|
||||
bool lIsString, rIsString;
|
||||
if ((lIsString = lval.isString()) | (rIsString = rval.isString())) {
|
||||
JSString *lstr, *rstr;
|
||||
|
@ -3563,15 +3553,15 @@ BEGIN_CASE(JSOP_ADD)
|
|||
JSString *str = js_ConcatStrings(cx, lstr, rstr);
|
||||
if (!str)
|
||||
goto error;
|
||||
regs.sp[-2].setString(str);
|
||||
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[-2].setNumber(l);
|
||||
regs.sp--;
|
||||
regs.sp[-1].setNumber(l);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4109,6 +4099,13 @@ BEGIN_CASE(JSOP_LENGTH)
|
|||
DO_NEXT_OP(len);
|
||||
}
|
||||
}
|
||||
|
||||
if (js_IsTypedArray(obj)) {
|
||||
TypedArray *tarray = TypedArray::fromJSObject(obj);
|
||||
regs.sp[-1].setNumber(tarray->length);
|
||||
len = JSOP_LENGTH_LENGTH;
|
||||
DO_NEXT_OP(len);
|
||||
}
|
||||
}
|
||||
|
||||
i = -2;
|
||||
|
@ -5984,17 +5981,17 @@ BEGIN_CASE(JSOP_SETLOCALPOP)
|
|||
}
|
||||
END_CASE(JSOP_SETLOCALPOP)
|
||||
|
||||
BEGIN_CASE(JSOP_IFPRIMTOP)
|
||||
BEGIN_CASE(JSOP_IFCANTCALLTOP)
|
||||
/*
|
||||
* 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()) {
|
||||
if (!js_IsCallable(regs.sp[-1])) {
|
||||
len = GET_JUMP_OFFSET(regs.pc);
|
||||
BRANCH(len);
|
||||
}
|
||||
END_CASE(JSOP_IFPRIMTOP)
|
||||
END_CASE(JSOP_IFCANTCALLTOP)
|
||||
|
||||
BEGIN_CASE(JSOP_PRIMTOP)
|
||||
JS_ASSERT(regs.sp > regs.fp()->base());
|
||||
|
|
|
@ -76,9 +76,9 @@
|
|||
#endif
|
||||
|
||||
#include "jsobjinlines.h"
|
||||
#include "jsstrinlines.h"
|
||||
|
||||
#include "vm/Stack-inl.h"
|
||||
#include "vm/String-inl.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::gc;
|
||||
|
@ -155,7 +155,7 @@ struct IdHashPolicy {
|
|||
}
|
||||
};
|
||||
|
||||
typedef HashSet<jsid, IdHashPolicy, ContextAllocPolicy> IdSet;
|
||||
typedef HashSet<jsid, IdHashPolicy> IdSet;
|
||||
|
||||
static inline bool
|
||||
NewKeyValuePair(JSContext *cx, jsid id, const Value &val, Value *rval)
|
||||
|
@ -172,37 +172,25 @@ 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);
|
||||
JS_ASSERT_IF(flags & JSITER_OWNONLY, obj == pobj);
|
||||
|
||||
/* If we've already seen this, we definitely won't add it. */
|
||||
if (JS_UNLIKELY(!!p))
|
||||
return true;
|
||||
if (!(flags & JSITER_OWNONLY) || pobj->isProxy()) {
|
||||
IdSet::AddPtr p = ht.lookupForAdd(id);
|
||||
JS_ASSERT_IF(obj == pobj && !obj->isProxy(), !p);
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
if ((pobj->getProto() || pobj->isProxy()) && !ht.add(p, id))
|
||||
return false;
|
||||
/* If we've already seen this, we definitely won't add it. */
|
||||
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 a proxy might return duplicated properties,
|
||||
* so always add if pobj is a proxy.
|
||||
*/
|
||||
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()) && !ht.add(p, id))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (enumerable || (flags & JSITER_HIDDEN))
|
||||
|
@ -223,8 +211,7 @@ EnumerateNativeProperties(JSContext *cx, JSObject *obj, JSObject *pobj, uintN fl
|
|||
|
||||
if (!JSID_IS_DEFAULT_XML_NAMESPACE(shape.propid) &&
|
||||
!shape.isAlias() &&
|
||||
!Enumerate(cx, obj, pobj, shape.propid, shape.enumerable(),
|
||||
shape.isSharedPermanent(), flags, ht, props))
|
||||
!Enumerate(cx, obj, pobj, shape.propid, shape.enumerable(), flags, ht, props))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -238,7 +225,7 @@ 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;
|
||||
}
|
||||
|
@ -249,7 +236,7 @@ EnumerateDenseArrayProperties(JSContext *cx, JSObject *obj, JSObject *pobj, uint
|
|||
for (size_t i = 0; i < capacity; ++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;
|
||||
}
|
||||
}
|
||||
|
@ -261,11 +248,6 @@ EnumerateDenseArrayProperties(JSContext *cx, JSObject *obj, JSObject *pobj, uint
|
|||
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;
|
||||
|
@ -299,7 +281,7 @@ Snapshot(JSContext *cx, JSObject *obj, uintN flags, AutoIdVector *props)
|
|||
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,13 +301,13 @@ 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);
|
||||
|
||||
|
@ -960,6 +942,9 @@ 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) {
|
||||
jsid id = ATOM_TO_JSID(cx->runtime->atomState.nextAtom);
|
||||
|
|
167
js/src/jslong.h
167
js/src/jslong.h
|
@ -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_<relational operators>
|
||||
**
|
||||
** 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_<logical operators>
|
||||
**
|
||||
** 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_<mathematical operators>
|
||||
**
|
||||
** 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_<mathematical operators>
|
||||
**
|
||||
** 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_<shifting operators>
|
||||
**
|
||||
** 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_<conversion operators>
|
||||
**
|
||||
** 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___ */
|
|
@ -43,7 +43,6 @@
|
|||
#include <stdlib.h>
|
||||
#include "jstypes.h"
|
||||
#include "jsstdint.h"
|
||||
#include "jslong.h"
|
||||
#include "prmjtime.h"
|
||||
#include "jsapi.h"
|
||||
#include "jsatom.h"
|
||||
|
|
|
@ -72,9 +72,12 @@
|
|||
#include "jsvector.h"
|
||||
|
||||
#include "jsinterpinlines.h"
|
||||
#include "jsnuminlines.h"
|
||||
#include "jsobjinlines.h"
|
||||
#include "jsstrinlines.h"
|
||||
|
||||
#include "vm/String-inl.h"
|
||||
|
||||
using namespace js;
|
||||
|
||||
#ifndef JS_HAVE_STDINT_H /* Native support is innocent until proven guilty. */
|
||||
|
@ -430,7 +433,7 @@ num_parseInt(JSContext *cx, uintN argc, Value *vp)
|
|||
return true;
|
||||
}
|
||||
/*
|
||||
* Step 1 is |inputString = ToString(string)|. When string >=
|
||||
* 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|.
|
||||
*
|
||||
|
@ -576,7 +579,7 @@ Number(JSContext *cx, uintN argc, Value *vp)
|
|||
|
||||
if (!isConstructing)
|
||||
return true;
|
||||
|
||||
|
||||
JSObject *obj = NewBuiltinClassInstance(cx, &js_NumberClass);
|
||||
if (!obj)
|
||||
return false;
|
||||
|
@ -980,7 +983,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[] = {
|
||||
|
@ -1017,11 +1020,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;
|
||||
|
@ -1044,41 +1047,65 @@ 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";
|
||||
|
||||
return rt->thousandsSeparator && rt->decimalSeparator && rt->numGrouping;
|
||||
/*
|
||||
* 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<char *>(OffTheBooks::malloc_(thousandsSeparatorSize +
|
||||
decimalPointSize +
|
||||
groupingSize));
|
||||
if (!storage)
|
||||
return false;
|
||||
|
||||
memcpy(storage, thousandsSeparator, thousandsSeparatorSize);
|
||||
rt->thousandsSeparator = storage;
|
||||
storage += thousandsSeparatorSize;
|
||||
|
||||
memcpy(storage, decimalPoint, decimalPointSize);
|
||||
rt->decimalSeparator = storage;
|
||||
storage += decimalPointSize;
|
||||
|
||||
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<char *>(rt->thousandsSeparator);
|
||||
Foreground::free_(storage);
|
||||
}
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
JSObject *
|
||||
js_InitNumberClass(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
|
@ -1140,7 +1167,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
|
||||
|
@ -1290,7 +1317,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;
|
||||
|
|
|
@ -47,7 +47,6 @@
|
|||
#include "jsvalue.h"
|
||||
|
||||
#include "jsstdint.h"
|
||||
#include "jsstr.h"
|
||||
#include "jsobj.h"
|
||||
|
||||
/*
|
||||
|
@ -150,12 +149,15 @@ 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 bool
|
||||
InitRuntimeNumberState(JSRuntime *rt);
|
||||
|
||||
extern void
|
||||
js_FinishRuntimeNumberState(JSContext *cx);
|
||||
FinishRuntimeNumberState(JSRuntime *rt);
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
/* Initialize the Number class, returning its prototype object. */
|
||||
extern js::Class js_NumberClass;
|
||||
|
@ -179,6 +181,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);
|
||||
|
||||
|
@ -469,7 +474,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.
|
||||
|
@ -627,76 +632,6 @@ ValueFitsInInt32(const Value &v, int32_t *pi)
|
|||
return v.isDouble() && JSDOUBLE_IS_INT32(v.toDouble(), pi);
|
||||
}
|
||||
|
||||
template<typename T> struct NumberTraits { };
|
||||
template<> struct NumberTraits<int32> {
|
||||
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<jsdouble> {
|
||||
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<typename T>
|
||||
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<T>::toSelfType(T(c - '0'));
|
||||
return true;
|
||||
}
|
||||
if (JS_ISSPACE(c)) {
|
||||
*result = NumberTraits<T>::toSelfType(T(0));
|
||||
return true;
|
||||
}
|
||||
*result = NumberTraits<T>::NaN();
|
||||
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<T>::NaN();
|
||||
return true;
|
||||
}
|
||||
*result = NumberTraits<T>::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) || js_SkipWhiteSpace(ep, end) != end) {
|
||||
*result = NumberTraits<T>::NaN();
|
||||
return true;
|
||||
}
|
||||
*result = NumberTraits<T>::toSelfType(d);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ES5 9.4 ToInteger. */
|
||||
static inline bool
|
||||
ToInteger(JSContext *cx, const js::Value &v, jsdouble *dp)
|
||||
|
|
|
@ -0,0 +1,119 @@
|
|||
/* -*- 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 "jsstr.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
template<typename T> struct NumberTraits { };
|
||||
template<> struct NumberTraits<int32> {
|
||||
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<jsdouble> {
|
||||
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<typename T>
|
||||
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<T>::toSelfType(T(c - '0'));
|
||||
return true;
|
||||
}
|
||||
if (JS_ISSPACE(c)) {
|
||||
*result = NumberTraits<T>::toSelfType(T(0));
|
||||
return true;
|
||||
}
|
||||
*result = NumberTraits<T>::NaN();
|
||||
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<T>::NaN();
|
||||
return true;
|
||||
}
|
||||
*result = NumberTraits<T>::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) || js_SkipWhiteSpace(ep, end) != end) {
|
||||
*result = NumberTraits<T>::NaN();
|
||||
return true;
|
||||
}
|
||||
*result = NumberTraits<T>::toSelfType(d);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace js
|
||||
|
||||
#endif /* jsnuminlines_h___ */
|
266
js/src/jsobj.cpp
266
js/src/jsobj.cpp
|
@ -917,16 +917,6 @@ 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)
|
||||
|
@ -1129,11 +1119,7 @@ EvalKernel(JSContext *cx, const CallArgs &call, EvalType evalType, StackFrame *c
|
|||
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)) {
|
||||
if (!scopeobj.getGlobal()->isRuntimeCodeGenEnabled(cx)) {
|
||||
JS_ReportError(cx, "call to eval() blocked by CSP");
|
||||
return false;
|
||||
}
|
||||
|
@ -1498,7 +1484,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);
|
||||
|
@ -1506,30 +1491,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<Shape *>(*propp);
|
||||
if (shape->isSharedPermanent())
|
||||
return true;
|
||||
}
|
||||
|
||||
if (outer != *objp)
|
||||
*propp = NULL;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1577,39 +1540,26 @@ js_PropertyIsEnumerable(JSContext *cx, JSObject *obj, jsid id, Value *vp)
|
|||
JSObject *pobj;
|
||||
JSProperty *prop;
|
||||
if (!obj->lookupProperty(cx, id, &pobj, &prop))
|
||||
return JS_FALSE;
|
||||
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->getAttributes(cx, id, &attrs))
|
||||
return false;
|
||||
|
||||
vp->setBoolean((attrs & JSPROP_ENUMERATE) != 0);
|
||||
return true;
|
||||
}
|
||||
|
@ -2096,23 +2046,6 @@ DefinePropertyOnObject(JSContext *cx, JSObject *obj, const jsid &id, const PropD
|
|||
|
||||
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())
|
||||
|
@ -2145,15 +2078,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<Shape *>(current);
|
||||
do {
|
||||
|
@ -5825,25 +5750,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);
|
||||
}
|
||||
|
@ -5905,30 +5813,49 @@ js_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, Value *rval, JSBool str
|
|||
|
||||
namespace js {
|
||||
|
||||
JSObject *
|
||||
HasNativeMethod(JSObject *obj, jsid methodid, Native native)
|
||||
bool
|
||||
HasDataProperty(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(methodid)) {
|
||||
if (shape->hasDefaultGetterOrIsMethod() && obj->containsSlot(shape->slot)) {
|
||||
*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 funobj;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
/*
|
||||
* 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 ExternalInvoke(cx, ObjectValue(*obj), *vp, 0, NULL, vp);
|
||||
}
|
||||
|
||||
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 == &js_StringClass &&
|
||||
ClassMethodIsNative(cx, obj,
|
||||
&js_StringClass,
|
||||
ATOM_TO_JSID(cx->runtime->atomState.toStringAtom),
|
||||
|
@ -5937,24 +5864,17 @@ DefaultValue(JSContext *cx, JSObject *obj, JSType hint, Value *vp)
|
|||
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,
|
||||
ATOM_TO_JSID(cx->runtime->atomState.valueOfAtom),
|
||||
|
@ -5967,44 +5887,30 @@ DefaultValue(JSContext *cx, JSObject *obj, JSType hint, Value *vp)
|
|||
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 */
|
||||
|
@ -6290,26 +6196,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
|
||||
|
|
|
@ -267,6 +267,10 @@ class NormalArgumentsObject;
|
|||
class StrictArgumentsObject;
|
||||
class StringObject;
|
||||
|
||||
/* ES5 8.12.8. */
|
||||
extern JSBool
|
||||
DefaultValue(JSContext *cx, JSObject *obj, JSType hint, Value *vp);
|
||||
|
||||
}
|
||||
|
||||
struct JSFunction;
|
||||
|
@ -1039,8 +1043,8 @@ struct JSObject : js::gc::Cell {
|
|||
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, ...).
|
||||
* Like init, but also initializes map. proto must have an empty shape
|
||||
* created for it via proto->getEmptyShape.
|
||||
*/
|
||||
inline bool initSharingEmptyShape(JSContext *cx,
|
||||
js::Class *clasp,
|
||||
|
@ -1178,6 +1182,11 @@ struct JSObject : js::gc::Cell {
|
|||
return (op ? op : js_Enumerate)(cx, this, iterop, statep, idp);
|
||||
}
|
||||
|
||||
bool defaultValue(JSContext *cx, JSType hint, js::Value *vp) {
|
||||
js::ConvertOp op = getClass()->convert;
|
||||
return (op == js::ConvertStub ? js::DefaultValue : op)(cx, this, hint, vp);
|
||||
}
|
||||
|
||||
JSType typeOf(JSContext *cx) {
|
||||
js::TypeOfOp op = getOps()->typeOf;
|
||||
return (op ? op : js_TypeOf)(cx, this);
|
||||
|
@ -1729,14 +1738,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(JSObject *obj, jsid methodid, js::Value *vp);
|
||||
|
||||
extern JSBool
|
||||
CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,
|
||||
|
@ -1780,18 +1786,37 @@ 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);
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
/*
|
||||
* 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.
|
||||
|
@ -1799,9 +1824,6 @@ ToObject(JSContext *cx, js::Value *vp)
|
|||
extern JSObject *
|
||||
js_ValueToNonNullObject(JSContext *cx, const js::Value &v);
|
||||
|
||||
extern JSBool
|
||||
js_TryValueOf(JSContext *cx, JSObject *obj, JSType type, js::Value *rval);
|
||||
|
||||
extern JSBool
|
||||
js_XDRObject(JSXDRState *xdr, JSObject **objp);
|
||||
|
||||
|
@ -1817,10 +1839,6 @@ 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);
|
||||
|
||||
|
|
|
@ -350,7 +350,7 @@ JSObject::slotsAndStructSize(uint32 nslots) const
|
|||
int nfslots = isFun ? 0 : numFixedSlots();
|
||||
|
||||
return sizeof(js::Value) * (ndslots + nfslots)
|
||||
+ isFun ? sizeof(JSFunction) : sizeof(JSObject);
|
||||
+ (isFun ? sizeof(JSFunction) : sizeof(JSObject));
|
||||
}
|
||||
|
||||
inline uint32
|
||||
|
@ -1370,28 +1370,6 @@ CopyInitializerObject(JSContext *cx, JSObject *baseobj)
|
|||
return obj;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
JS_ASSERT(obj->getClass() == clasp);
|
||||
|
||||
if (HasNativeMethod(obj, methodid, native))
|
||||
return true;
|
||||
|
||||
JSObject *pobj = obj->getProto();
|
||||
return pobj && pobj->getClass() == clasp &&
|
||||
HasNativeMethod(pobj, methodid, native);
|
||||
}
|
||||
|
||||
inline bool
|
||||
DefineConstructorAndPrototype(JSContext *cx, JSObject *global,
|
||||
JSProtoKey key, JSFunction *ctor, JSObject *proto)
|
||||
|
|
|
@ -505,8 +505,8 @@ OPDEF(JSOP_TYPEOFEXPR, 200,"typeofexpr", NULL, 1, 1, 1, 15, JOF_BYTE|J
|
|||
OPDEF(JSOP_ENTERBLOCK, 201,"enterblock", NULL, 3, 0, -1, 0, JOF_OBJECT)
|
||||
OPDEF(JSOP_LEAVEBLOCK, 202,"leaveblock", NULL, 5, -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)
|
||||
/* Jump to target if top of stack value isn't callable. */
|
||||
OPDEF(JSOP_IFCANTCALLTOP, 203,"ifcantcalltop",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)
|
||||
|
|
|
@ -184,7 +184,7 @@ JSParseNode::clear()
|
|||
pn_parens = false;
|
||||
}
|
||||
|
||||
Parser::Parser(JSContext *cx, JSPrincipals *prin, StackFrame *cfp)
|
||||
Parser::Parser(JSContext *cx, JSPrincipals *prin, StackFrame *cfp, bool foldConstants)
|
||||
: js::AutoGCRooter(cx, PARSER),
|
||||
context(cx),
|
||||
aleFreeList(NULL),
|
||||
|
@ -197,7 +197,8 @@ Parser::Parser(JSContext *cx, JSPrincipals *prin, StackFrame *cfp)
|
|||
traceListHead(NULL),
|
||||
tc(NULL),
|
||||
emptyCallShape(NULL),
|
||||
keepAtoms(cx->runtime)
|
||||
keepAtoms(cx->runtime),
|
||||
foldConstants(foldConstants)
|
||||
{
|
||||
js::PodArrayZero(tempFreeList);
|
||||
setPrincipals(prin);
|
||||
|
@ -767,7 +768,8 @@ JSParseNode::newBinaryOrAppend(TokenKind tt, JSOp op, JSParseNode *left, JSParse
|
|||
*/
|
||||
if (tt == TOK_PLUS &&
|
||||
left->pn_type == TOK_NUMBER &&
|
||||
right->pn_type == TOK_NUMBER) {
|
||||
right->pn_type == TOK_NUMBER &&
|
||||
tc->parser->foldConstants) {
|
||||
left->pn_dval += right->pn_dval;
|
||||
left->pn_pos.end = right->pn_pos.end;
|
||||
RecycleTree(right, tc);
|
||||
|
@ -861,7 +863,7 @@ Parser::parse(JSObject *chain)
|
|||
if (!tokenStream.matchToken(TOK_EOF)) {
|
||||
reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
|
||||
pn = NULL;
|
||||
} else {
|
||||
} else if (foldConstants) {
|
||||
if (!js_FoldConstants(context, pn, &globaltc))
|
||||
pn = NULL;
|
||||
}
|
||||
|
@ -1209,7 +1211,7 @@ Compiler::defineGlobals(JSContext *cx, GlobalScope &globalScope, JSScript *scrip
|
|||
def.knownSlot = shape->slot;
|
||||
}
|
||||
|
||||
js::Vector<JSScript *, 16, ContextAllocPolicy> worklist(cx);
|
||||
js::Vector<JSScript *, 16> worklist(cx);
|
||||
if (!worklist.append(script))
|
||||
return false;
|
||||
|
||||
|
@ -4155,8 +4157,10 @@ NoteLValue(JSContext *cx, JSParseNode *pn, JSTreeContext *tc, uintN dflag = PND_
|
|||
* chain; we ensure this happens by making such functions heavyweight.
|
||||
*/
|
||||
JSAtom *lname = pn->pn_atom;
|
||||
if (lname == cx->runtime->atomState.argumentsAtom ||
|
||||
(tc->inFunction() && lname == tc->fun()->atom)) {
|
||||
if (lname == cx->runtime->atomState.argumentsAtom) {
|
||||
tc->flags |= TCF_FUN_HEAVYWEIGHT;
|
||||
tc->countArgumentsUse(pn);
|
||||
} else if (tc->inFunction() && lname == tc->fun()->atom) {
|
||||
tc->flags |= TCF_FUN_HEAVYWEIGHT;
|
||||
}
|
||||
}
|
||||
|
@ -4617,8 +4621,9 @@ Parser::returnOrYield(bool useAssignExpr)
|
|||
JSParseNode *pn, *pn2;
|
||||
|
||||
tt = tokenStream.currentToken().type;
|
||||
if (tt == TOK_RETURN && !tc->inFunction()) {
|
||||
reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_RETURN_OR_YIELD, js_return_str);
|
||||
if (!tc->inFunction()) {
|
||||
reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_RETURN_OR_YIELD,
|
||||
(tt == TOK_RETURN) ? js_return_str : js_yield_str);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -4627,8 +4632,18 @@ Parser::returnOrYield(bool useAssignExpr)
|
|||
return NULL;
|
||||
|
||||
#if JS_HAS_GENERATORS
|
||||
if (tt == TOK_YIELD)
|
||||
tc->flags |= TCF_FUN_IS_GENERATOR;
|
||||
if (tt == TOK_YIELD) {
|
||||
/*
|
||||
* If we're within parens, we won't know if this is a generator expression until we see
|
||||
* a |for| token, so we have to delay flagging the current function.
|
||||
*/
|
||||
if (tc->parenDepth == 0) {
|
||||
tc->flags |= TCF_FUN_IS_GENERATOR;
|
||||
} else {
|
||||
tc->yieldCount++;
|
||||
tc->yieldNode = pn;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* This is ugly, but we don't want to require a semicolon. */
|
||||
|
@ -4841,6 +4856,10 @@ NewBindingNode(JSAtom *atom, JSTreeContext *tc, bool let = false)
|
|||
pn = NameNode::create(atom, tc);
|
||||
if (!pn)
|
||||
return NULL;
|
||||
|
||||
if (atom == tc->parser->context->runtime->atomState.argumentsAtom)
|
||||
tc->countArgumentsUse(pn);
|
||||
|
||||
return pn;
|
||||
}
|
||||
|
||||
|
@ -6213,7 +6232,7 @@ Parser::variables(bool inLetHead)
|
|||
|
||||
if (tc->inFunction() &&
|
||||
atom == context->runtime->atomState.argumentsAtom) {
|
||||
tc->noteArgumentsUse();
|
||||
tc->noteArgumentsUse(pn2);
|
||||
if (!let)
|
||||
tc->flags |= TCF_FUN_HEAVYWEIGHT;
|
||||
}
|
||||
|
@ -6645,7 +6664,7 @@ Parser::unaryExpr()
|
|||
* returns true. Here we fold constants before checking for a call
|
||||
* expression, in order to rule out delete of a generator expression.
|
||||
*/
|
||||
if (!js_FoldConstants(context, pn2, tc))
|
||||
if (foldConstants && !js_FoldConstants(context, pn2, tc))
|
||||
return NULL;
|
||||
switch (pn2->pn_type) {
|
||||
case TOK_LP:
|
||||
|
@ -6665,8 +6684,10 @@ Parser::unaryExpr()
|
|||
return NULL;
|
||||
}
|
||||
pn2->pn_op = JSOP_DELNAME;
|
||||
if (pn2->pn_atom == context->runtime->atomState.argumentsAtom)
|
||||
if (pn2->pn_atom == context->runtime->atomState.argumentsAtom) {
|
||||
tc->flags |= TCF_FUN_HEAVYWEIGHT;
|
||||
tc->countArgumentsUse(pn2);
|
||||
}
|
||||
break;
|
||||
default:;
|
||||
}
|
||||
|
@ -6740,6 +6761,65 @@ class CompExprTransplanter {
|
|||
bool transplant(JSParseNode *pn);
|
||||
};
|
||||
|
||||
/*
|
||||
* A helper for lazily checking for the presence of illegal |yield| or |arguments|
|
||||
* tokens inside of generator expressions.
|
||||
*
|
||||
* Use when entering a parenthesized context. If the nested expression is followed
|
||||
* by a |for| token (indicating that the parenthesized expression is a generator
|
||||
* expression), use the checkValidBody() method to see if any illegal tokens were
|
||||
* found.
|
||||
*/
|
||||
class GenexpGuard {
|
||||
JSTreeContext *tc;
|
||||
uint32 startYieldCount;
|
||||
uint32 startArgumentsCount;
|
||||
|
||||
public:
|
||||
explicit GenexpGuard(JSTreeContext *tc)
|
||||
: tc(tc)
|
||||
{
|
||||
if (tc->parenDepth == 0) {
|
||||
tc->yieldCount = tc->argumentsCount = 0;
|
||||
tc->yieldNode = tc->argumentsNode = NULL;
|
||||
}
|
||||
startYieldCount = tc->yieldCount;
|
||||
startArgumentsCount = tc->argumentsCount;
|
||||
tc->parenDepth++;
|
||||
}
|
||||
|
||||
void endBody();
|
||||
bool checkValidBody(JSParseNode *pn);
|
||||
};
|
||||
|
||||
void
|
||||
GenexpGuard::endBody()
|
||||
{
|
||||
tc->parenDepth--;
|
||||
}
|
||||
|
||||
bool
|
||||
GenexpGuard::checkValidBody(JSParseNode *pn)
|
||||
{
|
||||
if (tc->yieldCount > startYieldCount) {
|
||||
JSParseNode *errorNode = tc->yieldNode;
|
||||
if (!errorNode)
|
||||
errorNode = pn;
|
||||
tc->parser->reportErrorNumber(errorNode, JSREPORT_ERROR, JSMSG_BAD_GENEXP_BODY, js_yield_str);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tc->argumentsCount > startArgumentsCount) {
|
||||
JSParseNode *errorNode = tc->argumentsNode;
|
||||
if (!errorNode)
|
||||
errorNode = pn;
|
||||
tc->parser->reportErrorNumber(errorNode, JSREPORT_ERROR, JSMSG_BAD_GENEXP_BODY, js_arguments_str);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Any definitions nested within the comprehension expression of a generator
|
||||
* expression must move "down" one static level, which of course increases the
|
||||
|
@ -6940,7 +7020,7 @@ CompExprTransplanter::transplant(JSParseNode *pn)
|
|||
* (possibly nested) for-loop, initialized by |type, op, kid|.
|
||||
*/
|
||||
JSParseNode *
|
||||
Parser::comprehensionTail(JSParseNode *kid, uintN blockid,
|
||||
Parser::comprehensionTail(JSParseNode *kid, uintN blockid, bool isGenexp,
|
||||
TokenKind type, JSOp op)
|
||||
{
|
||||
uintN adjust;
|
||||
|
@ -7020,6 +7100,8 @@ Parser::comprehensionTail(JSParseNode *kid, uintN blockid,
|
|||
}
|
||||
MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
|
||||
|
||||
GenexpGuard guard(tc);
|
||||
|
||||
atom = NULL;
|
||||
tt = tokenStream.getToken();
|
||||
switch (tt) {
|
||||
|
@ -7062,6 +7144,11 @@ Parser::comprehensionTail(JSParseNode *kid, uintN blockid,
|
|||
return NULL;
|
||||
MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
|
||||
|
||||
guard.endBody();
|
||||
|
||||
if (isGenexp && !guard.checkValidBody(pn2))
|
||||
return NULL;
|
||||
|
||||
switch (tt) {
|
||||
#if JS_HAS_DESTRUCTURING
|
||||
case TOK_LB:
|
||||
|
@ -7197,7 +7284,7 @@ Parser::generatorExpr(JSParseNode *kid)
|
|||
genfn->pn_funbox = funbox;
|
||||
genfn->pn_blockid = gentc.bodyid;
|
||||
|
||||
JSParseNode *body = comprehensionTail(pn, outertc->blockid());
|
||||
JSParseNode *body = comprehensionTail(pn, outertc->blockid(), true);
|
||||
if (!body)
|
||||
return NULL;
|
||||
JS_ASSERT(!genfn->pn_body);
|
||||
|
@ -7234,6 +7321,8 @@ Parser::argumentList(JSParseNode *listNode)
|
|||
if (tokenStream.matchToken(TOK_RP, TSF_OPERAND))
|
||||
return JS_TRUE;
|
||||
|
||||
GenexpGuard guard(tc);
|
||||
|
||||
do {
|
||||
JSParseNode *argNode = assignExpr();
|
||||
if (!argNode)
|
||||
|
@ -7248,6 +7337,8 @@ Parser::argumentList(JSParseNode *listNode)
|
|||
#endif
|
||||
#if JS_HAS_GENERATOR_EXPRS
|
||||
if (tokenStream.matchToken(TOK_FOR)) {
|
||||
if (!guard.checkValidBody(argNode))
|
||||
return JS_FALSE;
|
||||
argNode = generatorExpr(argNode);
|
||||
if (!argNode)
|
||||
return JS_FALSE;
|
||||
|
@ -7266,6 +7357,7 @@ Parser::argumentList(JSParseNode *listNode)
|
|||
reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_PAREN_AFTER_ARGS);
|
||||
return JS_FALSE;
|
||||
}
|
||||
guard.endBody();
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
|
@ -8332,7 +8424,7 @@ Parser::primaryExpr(TokenKind tt, JSBool afterDot)
|
|||
pn->pn_tail = &pn->pn_head;
|
||||
*pn->pn_tail = NULL;
|
||||
|
||||
pntop = comprehensionTail(pnexp, pn->pn_blockid,
|
||||
pntop = comprehensionTail(pnexp, pn->pn_blockid, false,
|
||||
TOK_ARRAYPUSH, JSOP_ARRAYPUSH);
|
||||
if (!pntop)
|
||||
return NULL;
|
||||
|
@ -8640,7 +8732,7 @@ Parser::primaryExpr(TokenKind tt, JSBool afterDot)
|
|||
* a reference of the form foo.arguments, which ancient code may
|
||||
* still use instead of arguments (more hate).
|
||||
*/
|
||||
tc->noteArgumentsUse();
|
||||
tc->noteArgumentsUse(pn);
|
||||
|
||||
/*
|
||||
* Bind early to JSOP_ARGUMENTS to relieve later code from having
|
||||
|
@ -8655,6 +8747,12 @@ Parser::primaryExpr(TokenKind tt, JSBool afterDot)
|
|||
|| tokenStream.peekToken() == TOK_DBLCOLON
|
||||
#endif
|
||||
) && !(tc->flags & TCF_DECL_DESTRUCTURING)) {
|
||||
/* In case this is a generator expression outside of any function. */
|
||||
if (!tc->inFunction() &&
|
||||
pn->pn_atom == context->runtime->atomState.argumentsAtom) {
|
||||
tc->countArgumentsUse(pn);
|
||||
}
|
||||
|
||||
JSStmtInfo *stmt = js_LexicalLookup(tc, pn->pn_atom, NULL);
|
||||
|
||||
JSDefinition *dn;
|
||||
|
@ -8826,16 +8924,19 @@ Parser::parenExpr(JSBool *genexp)
|
|||
|
||||
if (genexp)
|
||||
*genexp = JS_FALSE;
|
||||
|
||||
GenexpGuard guard(tc);
|
||||
|
||||
pn = bracketedExpr();
|
||||
if (!pn)
|
||||
return NULL;
|
||||
guard.endBody();
|
||||
|
||||
#if JS_HAS_GENERATOR_EXPRS
|
||||
if (tokenStream.matchToken(TOK_FOR)) {
|
||||
if (pn->pn_type == TOK_YIELD && !pn->pn_parens) {
|
||||
reportErrorNumber(pn, JSREPORT_ERROR, JSMSG_BAD_GENERATOR_SYNTAX, js_yield_str);
|
||||
if (!guard.checkValidBody(pn))
|
||||
return NULL;
|
||||
}
|
||||
JS_ASSERT(pn->pn_type != TOK_YIELD);
|
||||
if (pn->pn_type == TOK_COMMA && !pn->pn_parens) {
|
||||
reportErrorNumber(pn->last(), JSREPORT_ERROR, JSMSG_BAD_GENERATOR_SYNTAX,
|
||||
js_generator_str);
|
||||
|
@ -8857,6 +8958,14 @@ Parser::parenExpr(JSBool *genexp)
|
|||
}
|
||||
#endif /* JS_HAS_GENERATOR_EXPRS */
|
||||
|
||||
if (tc->yieldCount > 0) {
|
||||
tc->flags |= TCF_FUN_IS_GENERATOR;
|
||||
if (!tc->inFunction()) {
|
||||
reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_RETURN_OR_YIELD, js_yield_str);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return pn;
|
||||
}
|
||||
|
||||
|
|
|
@ -651,7 +651,7 @@ public:
|
|||
JSParseNode *last() const {
|
||||
JS_ASSERT(pn_arity == PN_LIST);
|
||||
JS_ASSERT(pn_count != 0);
|
||||
return (JSParseNode *)((char *)pn_tail - offsetof(JSParseNode, pn_next));
|
||||
return (JSParseNode *)(uintptr_t(pn_tail) - offsetof(JSParseNode, pn_next));
|
||||
}
|
||||
|
||||
void makeEmpty() {
|
||||
|
@ -1065,7 +1065,10 @@ struct Parser : private js::AutoGCRooter
|
|||
/* Root atoms and objects allocated for the parsed tree. */
|
||||
js::AutoKeepAtoms keepAtoms;
|
||||
|
||||
Parser(JSContext *cx, JSPrincipals *prin = NULL, StackFrame *cfp = NULL);
|
||||
/* Perform constant-folding; must be true when interfacing with the emitter. */
|
||||
bool foldConstants;
|
||||
|
||||
Parser(JSContext *cx, JSPrincipals *prin = NULL, StackFrame *cfp = NULL, bool fold = true);
|
||||
~Parser();
|
||||
|
||||
friend void js::AutoGCRooter::trace(JSTracer *trc);
|
||||
|
@ -1197,7 +1200,7 @@ private:
|
|||
JSParseNode *functionDef(JSAtom *name, FunctionType type, uintN lambda);
|
||||
|
||||
JSParseNode *condition();
|
||||
JSParseNode *comprehensionTail(JSParseNode *kid, uintN blockid,
|
||||
JSParseNode *comprehensionTail(JSParseNode *kid, uintN blockid, bool isGenexp,
|
||||
js::TokenKind type = js::TOK_SEMI, JSOp op = JSOP_NOP);
|
||||
JSParseNode *generatorExpr(JSParseNode *kid);
|
||||
JSBool argumentList(JSParseNode *listNode);
|
||||
|
|
|
@ -47,7 +47,6 @@
|
|||
#include <stdlib.h>
|
||||
#include "jsprf.h"
|
||||
#include "jsstdint.h"
|
||||
#include "jslong.h"
|
||||
#include "jsutil.h"
|
||||
#include "jspubtd.h"
|
||||
#include "jsstr.h"
|
||||
|
@ -294,13 +293,8 @@ static int cvt_l(SprintfState *ss, long num, int width, int prec, int radix,
|
|||
static int cvt_ll(SprintfState *ss, JSInt64 num, int width, int prec, int radix,
|
||||
int type, int flags, const char *hexp)
|
||||
{
|
||||
char cvtbuf[100];
|
||||
char *cvt;
|
||||
int digits;
|
||||
JSInt64 rad;
|
||||
|
||||
/* according to the man page this needs to happen */
|
||||
if ((prec == 0) && (JSLL_IS_ZERO(num))) {
|
||||
if (prec == 0 && num == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -309,14 +303,14 @@ static int cvt_ll(SprintfState *ss, JSInt64 num, int width, int prec, int radix,
|
|||
** need to stop when we hit 10 digits. In the signed case, we can
|
||||
** stop when the number is zero.
|
||||
*/
|
||||
JSLL_I2L(rad, radix);
|
||||
cvt = cvtbuf + sizeof(cvtbuf);
|
||||
digits = 0;
|
||||
while (!JSLL_IS_ZERO(num)) {
|
||||
JSInt32 digit;
|
||||
JSInt64 quot, rem;
|
||||
JSLL_UDIVMOD(", &rem, num, rad);
|
||||
JSLL_L2I(digit, rem);
|
||||
JSInt64 rad = JSInt64(radix);
|
||||
char cvtbuf[100];
|
||||
char *cvt = cvtbuf + sizeof(cvtbuf);
|
||||
int digits = 0;
|
||||
while (num != 0) {
|
||||
JSInt64 quot = JSUint64(num) / rad;
|
||||
JSInt64 rem = JSUint64(num) % rad;
|
||||
JSInt32 digit = JSInt32(rem);
|
||||
*--cvt = hexp[digit & 0xf];
|
||||
digits++;
|
||||
num = quot;
|
||||
|
@ -899,8 +893,8 @@ static int dosprintf(SprintfState *ss, const char *fmt, va_list ap)
|
|||
|
||||
case TYPE_INT64:
|
||||
u.ll = va_arg(ap, JSInt64);
|
||||
if (!JSLL_GE_ZERO(u.ll)) {
|
||||
JSLL_NEG(u.ll, u.ll);
|
||||
if (u.ll < 0) {
|
||||
u.ll = -u.ll;
|
||||
flags |= FLAG_NEG;
|
||||
}
|
||||
goto do_longlong;
|
||||
|
|
|
@ -264,6 +264,12 @@ JSProxyHandler::fun_toString(JSContext *cx, JSObject *proxy, uintN indent)
|
|||
return fun_toStringHelper(cx, &fval.toObject(), indent);
|
||||
}
|
||||
|
||||
bool
|
||||
JSProxyHandler::defaultValue(JSContext *cx, JSObject *proxy, JSType hint, Value *vp)
|
||||
{
|
||||
return DefaultValue(cx, proxy, hint, vp);
|
||||
}
|
||||
|
||||
bool
|
||||
JSProxyHandler::call(JSContext *cx, JSObject *proxy, uintN argc, Value *vp)
|
||||
{
|
||||
|
@ -880,6 +886,14 @@ JSProxy::fun_toString(JSContext *cx, JSObject *proxy, uintN indent)
|
|||
return proxy->getProxyHandler()->fun_toString(cx, proxy, indent);
|
||||
}
|
||||
|
||||
bool
|
||||
JSProxy::defaultValue(JSContext *cx, JSObject *proxy, JSType hint, Value *vp)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return NULL);
|
||||
AutoPendingProxyOperation pending(cx, proxy);
|
||||
return proxy->getProxyHandler()->defaultValue(cx, proxy, hint, vp);
|
||||
}
|
||||
|
||||
static JSObject *
|
||||
proxy_innerObject(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
|
@ -982,6 +996,13 @@ proxy_TraceFunction(JSTracer *trc, JSObject *obj)
|
|||
MarkValue(trc, GetConstruct(obj), "construct");
|
||||
}
|
||||
|
||||
static JSBool
|
||||
proxy_Convert(JSContext *cx, JSObject *proxy, JSType hint, Value *vp)
|
||||
{
|
||||
JS_ASSERT(proxy->isProxy());
|
||||
return JSProxy::defaultValue(cx, proxy, hint, vp);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
proxy_Fix(JSContext *cx, JSObject *obj, bool *fixed, AutoIdVector *props)
|
||||
{
|
||||
|
@ -1030,7 +1051,7 @@ JS_FRIEND_API(Class) ObjectProxyClass = {
|
|||
StrictPropertyStub, /* setProperty */
|
||||
EnumerateStub,
|
||||
ResolveStub,
|
||||
ConvertStub,
|
||||
proxy_Convert,
|
||||
proxy_Finalize, /* finalize */
|
||||
NULL, /* reserved0 */
|
||||
NULL, /* checkAccess */
|
||||
|
@ -1311,7 +1332,7 @@ callable_Call(JSContext *cx, uintN argc, Value *vp)
|
|||
return ok;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
JSBool
|
||||
callable_Construct(JSContext *cx, uintN argc, Value *vp)
|
||||
{
|
||||
JSObject *thisobj = js_CreateThis(cx, &JS_CALLEE(cx, vp).toObject());
|
||||
|
|
|
@ -84,6 +84,7 @@ class JS_FRIEND_API(JSProxyHandler) {
|
|||
virtual JSType typeOf(JSContext *cx, JSObject *proxy);
|
||||
virtual JSString *obj_toString(JSContext *cx, JSObject *proxy);
|
||||
virtual JSString *fun_toString(JSContext *cx, JSObject *proxy, uintN indent);
|
||||
virtual bool defaultValue(JSContext *cx, JSObject *obj, JSType hint, js::Value *vp);
|
||||
virtual void finalize(JSContext *cx, JSObject *proxy);
|
||||
virtual void trace(JSTracer *trc, JSObject *proxy);
|
||||
|
||||
|
@ -130,6 +131,7 @@ class JSProxy {
|
|||
static JSType typeOf(JSContext *cx, JSObject *proxy);
|
||||
static JSString *obj_toString(JSContext *cx, JSObject *proxy);
|
||||
static JSString *fun_toString(JSContext *cx, JSObject *proxy, uintN indent);
|
||||
static bool defaultValue(JSContext *cx, JSObject *obj, JSType hint, js::Value *vp);
|
||||
};
|
||||
|
||||
/* Shared between object and function proxies. */
|
||||
|
|
|
@ -158,12 +158,11 @@ struct Token;
|
|||
struct TokenPos;
|
||||
struct TokenPtr;
|
||||
|
||||
class ContextAllocPolicy;
|
||||
class SystemAllocPolicy;
|
||||
class TempAllocPolicy;
|
||||
|
||||
template <class T,
|
||||
size_t MinInlineCapacity = 0,
|
||||
class AllocPolicy = ContextAllocPolicy>
|
||||
class AllocPolicy = TempAllocPolicy>
|
||||
class Vector;
|
||||
|
||||
template <class>
|
||||
|
@ -172,12 +171,12 @@ struct DefaultHasher;
|
|||
template <class Key,
|
||||
class Value,
|
||||
class HashPolicy = DefaultHasher<Key>,
|
||||
class AllocPolicy = ContextAllocPolicy>
|
||||
class AllocPolicy = TempAllocPolicy>
|
||||
class HashMap;
|
||||
|
||||
template <class T,
|
||||
class HashPolicy = DefaultHasher<T>,
|
||||
class AllocPolicy = ContextAllocPolicy>
|
||||
class AllocPolicy = TempAllocPolicy>
|
||||
class HashSet;
|
||||
|
||||
class PropertyCache;
|
||||
|
|
|
@ -1624,7 +1624,6 @@ class ASTSerializer
|
|||
bool expressions(JSParseNode *pn, NodeVector &elts);
|
||||
bool xmls(JSParseNode *pn, NodeVector &elts);
|
||||
bool leftAssociate(JSParseNode *pn, Value *dst);
|
||||
bool binaryOperands(JSParseNode *pn, NodeVector &elts);
|
||||
bool functionArgs(JSParseNode *pn, JSParseNode *pnargs, JSParseNode *pndestruct,
|
||||
JSParseNode *pnbody, NodeVector &args);
|
||||
|
||||
|
@ -2108,6 +2107,7 @@ ASTSerializer::forInit(JSParseNode *pn, Value *dst)
|
|||
bool
|
||||
ASTSerializer::statement(JSParseNode *pn, Value *dst)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
switch (PN_TYPE(pn)) {
|
||||
case TOK_FUNCTION:
|
||||
case TOK_VAR:
|
||||
|
@ -2352,23 +2352,6 @@ ASTSerializer::leftAssociate(JSParseNode *pn, Value *dst)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ASTSerializer::binaryOperands(JSParseNode *pn, NodeVector &elts)
|
||||
{
|
||||
if (pn->pn_arity == PN_BINARY) {
|
||||
Value left, right;
|
||||
|
||||
return expression(pn->pn_left, &left) &&
|
||||
elts.append(left) &&
|
||||
expression(pn->pn_right, &right) &&
|
||||
elts.append(right);
|
||||
}
|
||||
|
||||
LOCAL_ASSERT(pn->pn_arity == PN_LIST);
|
||||
|
||||
return expressions(pn, elts);
|
||||
}
|
||||
|
||||
bool
|
||||
ASTSerializer::comprehensionBlock(JSParseNode *pn, Value *dst)
|
||||
{
|
||||
|
@ -2457,6 +2440,7 @@ ASTSerializer::generatorExpression(JSParseNode *pn, Value *dst)
|
|||
bool
|
||||
ASTSerializer::expression(JSParseNode *pn, Value *dst)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
switch (PN_TYPE(pn)) {
|
||||
case TOK_FUNCTION:
|
||||
return function(pn, AST_FUNC_EXPR, dst);
|
||||
|
@ -2753,6 +2737,7 @@ ASTSerializer::expression(JSParseNode *pn, Value *dst)
|
|||
bool
|
||||
ASTSerializer::xml(JSParseNode *pn, Value *dst)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
switch (PN_TYPE(pn)) {
|
||||
#ifdef JS_HAS_XML_SUPPORT
|
||||
case TOK_LC:
|
||||
|
@ -2975,6 +2960,7 @@ ASTSerializer::objectPattern(JSParseNode *pn, VarDeclKind *pkind, Value *dst)
|
|||
bool
|
||||
ASTSerializer::pattern(JSParseNode *pn, VarDeclKind *pkind, Value *dst)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
switch (PN_TYPE(pn)) {
|
||||
case TOK_RC:
|
||||
return objectPattern(pn, pkind, dst);
|
||||
|
@ -3274,7 +3260,7 @@ reflect_parse(JSContext *cx, uint32 argc, jsval *vp)
|
|||
if (!chars)
|
||||
return JS_FALSE;
|
||||
|
||||
Parser parser(cx);
|
||||
Parser parser(cx, NULL, NULL, false);
|
||||
|
||||
if (!parser.init(chars, length, filename, lineno, cx->findVersion()))
|
||||
return JS_FALSE;
|
||||
|
|
|
@ -1350,12 +1350,12 @@ TokenStream::getTokenInternal()
|
|||
* early allows subsequent checking to be faster.
|
||||
*/
|
||||
if (JS_UNLIKELY(c >= 128)) {
|
||||
if (JS_ISSPACE_OR_BOM(c))
|
||||
goto retry;
|
||||
if (JS_ISSPACE_OR_BOM(c)) {
|
||||
if (c == LINE_SEPARATOR || c == PARA_SEPARATOR) {
|
||||
updateLineInfoForEOL();
|
||||
updateFlagsForEOL();
|
||||
}
|
||||
|
||||
if (c == LINE_SEPARATOR || c == PARA_SEPARATOR) {
|
||||
updateLineInfoForEOL();
|
||||
updateFlagsForEOL();
|
||||
goto retry;
|
||||
}
|
||||
|
||||
|
@ -1578,6 +1578,10 @@ TokenStream::getTokenInternal()
|
|||
+ JS7_UNHEX(cp[2])) << 4)
|
||||
+ JS7_UNHEX(cp[3]);
|
||||
skipChars(4);
|
||||
} else {
|
||||
ReportCompileErrorNumber(cx, this, NULL, JSREPORT_ERROR,
|
||||
JSMSG_MALFORMED_ESCAPE, "Unicode");
|
||||
goto error;
|
||||
}
|
||||
} else if (c == 'x') {
|
||||
jschar cp[2];
|
||||
|
@ -1585,9 +1589,16 @@ TokenStream::getTokenInternal()
|
|||
JS7_ISHEX(cp[0]) && JS7_ISHEX(cp[1])) {
|
||||
c = (JS7_UNHEX(cp[0]) << 4) + JS7_UNHEX(cp[1]);
|
||||
skipChars(2);
|
||||
} else {
|
||||
ReportCompileErrorNumber(cx, this, NULL, JSREPORT_ERROR,
|
||||
JSMSG_MALFORMED_ESCAPE, "hexadecimal");
|
||||
goto error;
|
||||
}
|
||||
} else if (c == '\n') {
|
||||
/* ECMA follows C by removing escaped newlines. */
|
||||
/*
|
||||
* ES5 7.8.4: an escaped line terminator represents
|
||||
* no character.
|
||||
*/
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -53,7 +53,6 @@
|
|||
#include "jscntxt.h"
|
||||
#include "jscompartment.h"
|
||||
#include "jshashtable.h"
|
||||
#include "jsiter.h"
|
||||
#include "jsobj.h"
|
||||
#include "jsprvtd.h"
|
||||
#include "jspubtd.h"
|
||||
|
@ -556,8 +555,6 @@ struct Shape : public js::gc::Cell
|
|||
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;
|
||||
|
||||
bool hasSlot() const { return (attrs & JSPROP_SHARED) == 0; }
|
||||
|
||||
uint8 attributes() const { return attrs; }
|
||||
|
@ -638,25 +635,11 @@ struct EmptyShape : public js::Shape
|
|||
|
||||
static inline EmptyShape *getEmptyArgumentsShape(JSContext *cx);
|
||||
|
||||
static EmptyShape *getEmptyBlockShape(JSContext *cx) {
|
||||
return ensure(cx, &js_BlockClass, &cx->compartment->emptyBlockShape);
|
||||
}
|
||||
|
||||
static EmptyShape *getEmptyCallShape(JSContext *cx) {
|
||||
return ensure(cx, &js_CallClass, &cx->compartment->emptyCallShape);
|
||||
}
|
||||
|
||||
static EmptyShape *getEmptyDeclEnvShape(JSContext *cx) {
|
||||
return ensure(cx, &js_DeclEnvClass, &cx->compartment->emptyDeclEnvShape);
|
||||
}
|
||||
|
||||
static EmptyShape *getEmptyEnumeratorShape(JSContext *cx) {
|
||||
return ensure(cx, &js_IteratorClass, &cx->compartment->emptyEnumeratorShape);
|
||||
}
|
||||
|
||||
static EmptyShape *getEmptyWithShape(JSContext *cx) {
|
||||
return ensure(cx, &js_WithClass, &cx->compartment->emptyWithShape);
|
||||
}
|
||||
static inline EmptyShape *getEmptyBlockShape(JSContext *cx);
|
||||
static inline EmptyShape *getEmptyCallShape(JSContext *cx);
|
||||
static inline EmptyShape *getEmptyDeclEnvShape(JSContext *cx);
|
||||
static inline EmptyShape *getEmptyEnumeratorShape(JSContext *cx);
|
||||
static inline EmptyShape *getEmptyWithShape(JSContext *cx);
|
||||
};
|
||||
|
||||
} /* namespace js */
|
||||
|
@ -775,12 +758,6 @@ Shape::search(JSRuntime *rt, js::Shape **startp, jsid id, bool adding)
|
|||
|
||||
#undef METER
|
||||
|
||||
inline bool
|
||||
Shape::isSharedPermanent() const
|
||||
{
|
||||
return (~attrs & (JSPROP_SHARED | JSPROP_PERMANENT)) == 0;
|
||||
}
|
||||
|
||||
} // namespace js
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#define jsscopeinlines_h___
|
||||
|
||||
#include <new>
|
||||
#include "jsarray.h"
|
||||
#include "jsbool.h"
|
||||
#include "jscntxt.h"
|
||||
#include "jsdbgapi.h"
|
||||
|
@ -362,6 +363,35 @@ EmptyShape::getEmptyArgumentsShape(JSContext *cx)
|
|||
return ensure(cx, &NormalArgumentsObject::jsClass, &cx->compartment->emptyArgumentsShape);
|
||||
}
|
||||
|
||||
/* static */ inline EmptyShape *
|
||||
EmptyShape::getEmptyBlockShape(JSContext *cx)
|
||||
{
|
||||
return ensure(cx, &js_BlockClass, &cx->compartment->emptyBlockShape);
|
||||
}
|
||||
|
||||
/* static */ inline EmptyShape *
|
||||
EmptyShape::getEmptyCallShape(JSContext *cx)
|
||||
{
|
||||
return ensure(cx, &js_CallClass, &cx->compartment->emptyCallShape);
|
||||
}
|
||||
|
||||
/* static */ inline EmptyShape *
|
||||
EmptyShape::getEmptyDeclEnvShape(JSContext *cx)
|
||||
{
|
||||
return ensure(cx, &js_DeclEnvClass, &cx->compartment->emptyDeclEnvShape);
|
||||
}
|
||||
|
||||
/* static */ inline EmptyShape *
|
||||
EmptyShape::getEmptyEnumeratorShape(JSContext *cx)
|
||||
{
|
||||
return ensure(cx, &js_IteratorClass, &cx->compartment->emptyEnumeratorShape);
|
||||
}
|
||||
|
||||
/* static */ inline EmptyShape *
|
||||
EmptyShape::getEmptyWithShape(JSContext *cx)
|
||||
{
|
||||
return ensure(cx, &js_WithClass, &cx->compartment->emptyWithShape);
|
||||
}
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
|
|
|
@ -320,7 +320,6 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp)
|
|||
uint16 nClosedArgs = 0, nClosedVars = 0;
|
||||
JSPrincipals *principals;
|
||||
uint32 encodeable;
|
||||
jssrcnote *sn;
|
||||
JSSecurityCallbacks *callbacks;
|
||||
uint32 scriptBits = 0;
|
||||
|
||||
|
@ -471,12 +470,8 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp)
|
|||
nslots = (uint32)((script->staticLevel << 16) | script->nslots);
|
||||
natoms = (uint32)script->atomMap.length;
|
||||
|
||||
/* 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;
|
||||
|
@ -855,50 +850,40 @@ static JSHashAllocOps sftbl_alloc_ops = {
|
|||
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
|
||||
}
|
||||
namespace js {
|
||||
|
||||
JSBool
|
||||
js_InitRuntimeScriptState(JSRuntime *rt)
|
||||
bool
|
||||
InitRuntimeScriptState(JSRuntime *rt)
|
||||
{
|
||||
#ifdef JS_THREADSAFE
|
||||
JS_ASSERT(!rt->scriptFilenameTableLock);
|
||||
rt->scriptFilenameTableLock = JS_NEW_LOCK();
|
||||
if (!rt->scriptFilenameTableLock)
|
||||
return JS_FALSE;
|
||||
return 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;
|
||||
}
|
||||
return JS_TRUE;
|
||||
if (!rt->scriptFilenameTable)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
js_FreeRuntimeScriptState(JSRuntime *rt)
|
||||
FreeRuntimeScriptState(JSRuntime *rt)
|
||||
{
|
||||
if (!rt->scriptFilenameTable)
|
||||
return;
|
||||
|
||||
FinishRuntimeScriptState(rt);
|
||||
if (rt->scriptFilenameTable)
|
||||
JS_HashTableDestroy(rt->scriptFilenameTable);
|
||||
#ifdef JS_THREADSAFE
|
||||
if (rt->scriptFilenameTableLock)
|
||||
JS_DESTROY_LOCK(rt->scriptFilenameTableLock);
|
||||
#endif
|
||||
}
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
static const char *
|
||||
SaveScriptFilename(JSContext *cx, const char *filename)
|
||||
{
|
||||
|
@ -1078,7 +1063,7 @@ JSScript::NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natom
|
|||
|
||||
size = sizeof(JSScript) +
|
||||
sizeof(JSAtom *) * natoms;
|
||||
|
||||
|
||||
if (nobjects != 0)
|
||||
size += sizeof(JSObjectArray) + nobjects * sizeof(JSObject *);
|
||||
if (nupvars != 0)
|
||||
|
@ -1422,6 +1407,29 @@ bad:
|
|||
return NULL;
|
||||
}
|
||||
|
||||
size_t
|
||||
JSScript::totalSize()
|
||||
{
|
||||
return code +
|
||||
length * sizeof(jsbytecode) +
|
||||
numNotes() * sizeof(jssrcnote) -
|
||||
(uint8 *) this;
|
||||
}
|
||||
|
||||
/*
|
||||
* Nb: srcnotes are variable-length. This function computes the number of
|
||||
* srcnote *slots*, which may be greater than the number of srcnotes.
|
||||
*/
|
||||
uint32
|
||||
JSScript::numNotes()
|
||||
{
|
||||
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 */
|
||||
}
|
||||
|
||||
JS_FRIEND_API(void)
|
||||
js_CallNewScriptHook(JSContext *cx, JSScript *script, JSFunction *fun)
|
||||
{
|
||||
|
|
|
@ -330,7 +330,7 @@ class Bindings {
|
|||
*
|
||||
* (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
|
||||
|
@ -458,16 +458,17 @@ struct JSScript {
|
|||
private:
|
||||
uint16 version; /* JS version under which script was compiled */
|
||||
|
||||
size_t callCount_; /* Number of times the script has been called. */
|
||||
|
||||
public:
|
||||
uint16 nfixed; /* number of slots besides stack operands in
|
||||
slot array */
|
||||
private:
|
||||
size_t callCount_; /* Number of times the script has been called. */
|
||||
|
||||
/*
|
||||
* Offsets to various array structures from the end of this script, or
|
||||
* JSScript::INVALID_OFFSET if the array has length 0.
|
||||
*/
|
||||
public:
|
||||
uint8 objectsOffset; /* offset to the array of nested function,
|
||||
block, scope, xml and one-time regexps
|
||||
objects */
|
||||
|
@ -576,6 +577,9 @@ struct JSScript {
|
|||
}
|
||||
#endif
|
||||
|
||||
JS_FRIEND_API(size_t) totalSize(); /* Size of the JSScript and all sections */
|
||||
uint32 numNotes(); /* Number of srcnote slots in the srcnotes section */
|
||||
|
||||
/* Script notes are allocated right after the code. */
|
||||
jssrcnote *notes() { return (jssrcnote *)(code + length); }
|
||||
|
||||
|
@ -584,32 +588,32 @@ struct JSScript {
|
|||
|
||||
JSObjectArray *objects() {
|
||||
JS_ASSERT(isValidOffset(objectsOffset));
|
||||
return (JSObjectArray *)((uint8 *) (this + 1) + objectsOffset);
|
||||
return reinterpret_cast<JSObjectArray *>(uintptr_t(this + 1) + objectsOffset);
|
||||
}
|
||||
|
||||
JSUpvarArray *upvars() {
|
||||
JS_ASSERT(isValidOffset(upvarsOffset));
|
||||
return (JSUpvarArray *) ((uint8 *) (this + 1) + upvarsOffset);
|
||||
return reinterpret_cast<JSUpvarArray *>(uintptr_t(this + 1) + upvarsOffset);
|
||||
}
|
||||
|
||||
JSObjectArray *regexps() {
|
||||
JS_ASSERT(isValidOffset(regexpsOffset));
|
||||
return (JSObjectArray *) ((uint8 *) (this + 1) + regexpsOffset);
|
||||
return reinterpret_cast<JSObjectArray *>(uintptr_t(this + 1) + regexpsOffset);
|
||||
}
|
||||
|
||||
JSTryNoteArray *trynotes() {
|
||||
JS_ASSERT(isValidOffset(trynotesOffset));
|
||||
return (JSTryNoteArray *) ((uint8 *) (this + 1) + trynotesOffset);
|
||||
return reinterpret_cast<JSTryNoteArray *>(uintptr_t(this + 1) + trynotesOffset);
|
||||
}
|
||||
|
||||
js::GlobalSlotArray *globals() {
|
||||
JS_ASSERT(isValidOffset(globalsOffset));
|
||||
return (js::GlobalSlotArray *) ((uint8 *) (this + 1) + globalsOffset);
|
||||
return reinterpret_cast<js::GlobalSlotArray *>(uintptr_t(this + 1) + globalsOffset);
|
||||
}
|
||||
|
||||
JSConstArray *consts() {
|
||||
JS_ASSERT(isValidOffset(constOffset));
|
||||
return (JSConstArray *) ((uint8 *) (this + 1) + constOffset);
|
||||
return reinterpret_cast<JSConstArray *>(uintptr_t(this + 1) + constOffset);
|
||||
}
|
||||
|
||||
JSAtom *getAtom(size_t index) {
|
||||
|
@ -699,12 +703,10 @@ 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);
|
||||
namespace js {
|
||||
|
||||
extern bool
|
||||
InitRuntimeScriptState(JSRuntime *rt);
|
||||
|
||||
/*
|
||||
* On JS_DestroyRuntime(rt), forcibly free script filename prefixes and any
|
||||
|
@ -713,7 +715,9 @@ js_InitRuntimeScriptState(JSRuntime *rt);
|
|||
* This allows script filename prefixes to outlive any context in rt.
|
||||
*/
|
||||
extern void
|
||||
js_FreeRuntimeScriptState(JSRuntime *rt);
|
||||
FreeRuntimeScriptState(JSRuntime *rt);
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
extern void
|
||||
js_MarkScriptFilename(const char *filename);
|
||||
|
|
299
js/src/jsstr.cpp
299
js/src/jsstr.cpp
|
@ -79,299 +79,14 @@
|
|||
#include "jsinterpinlines.h"
|
||||
#include "jsobjinlines.h"
|
||||
#include "jsregexpinlines.h"
|
||||
#include "jsstrinlines.h"
|
||||
#include "jsautooplen.h" // generated headers last
|
||||
|
||||
#include "vm/StringObject-inl.h"
|
||||
#include "vm/String-inl.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::gc;
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
bool
|
||||
JSString::isInline() const
|
||||
{
|
||||
return isFixed() && (d.u1.chars == d.inlineStorage || isShort());
|
||||
}
|
||||
|
||||
bool
|
||||
JSString::isExternal() const
|
||||
{
|
||||
bool is_external = arenaHeader()->getThingKind() == FINALIZE_EXTERNAL_STRING;
|
||||
JS_ASSERT_IF(is_external, isFixed());
|
||||
return is_external;
|
||||
}
|
||||
|
||||
void
|
||||
JSLinearString::mark(JSTracer *)
|
||||
{
|
||||
JSLinearString *str = this;
|
||||
while (!str->isStaticAtom() && str->markIfUnmarked() && str->isDependent())
|
||||
str = str->asDependent().base();
|
||||
}
|
||||
|
||||
size_t
|
||||
JSString::charsHeapSize()
|
||||
{
|
||||
/* JSRope: do nothing, we'll count all children chars when we hit the leaf strings. */
|
||||
if (isRope())
|
||||
return 0;
|
||||
|
||||
JS_ASSERT(isLinear());
|
||||
|
||||
/* JSDependentString: do nothing, we'll count the chars when we hit the base string. */
|
||||
if (isDependent())
|
||||
return 0;
|
||||
|
||||
JS_ASSERT(isFlat());
|
||||
|
||||
/* JSExtensibleString: count the full capacity, not just the used space. */
|
||||
if (isExtensible())
|
||||
return asExtensible().capacity() * sizeof(jschar);
|
||||
|
||||
JS_ASSERT(isFixed());
|
||||
|
||||
/* JSExternalString: don't count, the chars could be stored anywhere. */
|
||||
if (isExternal())
|
||||
return 0;
|
||||
|
||||
/* JSInlineString, JSShortString, JSInlineAtom, JSShortAtom: the chars are inline. */
|
||||
if (isInline())
|
||||
return 0;
|
||||
|
||||
/* JSStaticAtom: the chars are static and so not part of the heap. */
|
||||
if (isStaticAtom())
|
||||
return 0;
|
||||
|
||||
/* JSAtom, JSFixedString: count the chars. */
|
||||
return length() * sizeof(jschar);
|
||||
}
|
||||
|
||||
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<jschar *>(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;
|
||||
|
||||
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;
|
||||
|
||||
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
|
||||
|
@ -390,7 +105,7 @@ ArgToRootedString(JSContext *cx, uintN argc, Value *vp, uintN arg)
|
|||
return cx->runtime->atomState.typeAtoms[JSTYPE_VOID];
|
||||
vp += 2 + arg;
|
||||
|
||||
if (vp->isObject() && !DefaultValue(cx, &vp->toObject(), JSTYPE_STRING, vp))
|
||||
if (!ToPrimitive(cx, JSTYPE_STRING, vp))
|
||||
return NULL;
|
||||
|
||||
JSLinearString *str;
|
||||
|
@ -3231,7 +2946,7 @@ static JSFunctionSpec string_methods[] = {
|
|||
*/
|
||||
#define R(c) { \
|
||||
BUILD_LENGTH_AND_FLAGS(1, JSString::STATIC_ATOM_FLAGS), \
|
||||
{ (jschar *)(((char *)(unitStaticTable + (c))) + \
|
||||
{ (jschar *)(uintptr_t(unitStaticTable + (c)) + \
|
||||
offsetof(JSString::Data, inlineStorage)) }, \
|
||||
{ {(c), 0x00} } }
|
||||
|
||||
|
@ -3307,7 +3022,7 @@ const jschar JSAtom::fromSmallChar[] = { R6(0) };
|
|||
*/
|
||||
#define R(c) { \
|
||||
BUILD_LENGTH_AND_FLAGS(2, JSString::STATIC_ATOM_FLAGS), \
|
||||
{ (jschar *)(((char *)(length2StaticTable + (c))) + \
|
||||
{ (jschar *)(uintptr_t(length2StaticTable + (c)) + \
|
||||
offsetof(JSString::Data, inlineStorage)) }, \
|
||||
{ {FROM_SMALL_CHAR((c) >> 6), FROM_SMALL_CHAR((c) & 0x3F), 0x00} } }
|
||||
|
||||
|
@ -3344,7 +3059,7 @@ __attribute__ ((aligned (8)))
|
|||
*/
|
||||
#define R(c) { \
|
||||
BUILD_LENGTH_AND_FLAGS(3, JSString::STATIC_ATOM_FLAGS), \
|
||||
{ (jschar *)(((char *)(hundredStaticTable + ((c) - 100))) + \
|
||||
{ (jschar *)(uintptr_t(hundredStaticTable + ((c) - 100)) + \
|
||||
offsetof(JSString::Data, inlineStorage)) }, \
|
||||
{ {((c) / 100) + '0', ((c) / 10 % 10) + '0', ((c) % 10) + '0', 0x00} } }
|
||||
|
||||
|
@ -3805,7 +3520,7 @@ JSString *
|
|||
js_ValueToString(JSContext *cx, const Value &arg)
|
||||
{
|
||||
Value v = arg;
|
||||
if (v.isObject() && !DefaultValue(cx, &v.toObject(), JSTYPE_STRING, &v))
|
||||
if (!ToPrimitive(cx, JSTYPE_STRING, &v))
|
||||
return NULL;
|
||||
|
||||
JSString *str;
|
||||
|
@ -3830,7 +3545,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())
|
||||
|
|
702
js/src/jsstr.h
702
js/src/jsstr.h
|
@ -49,708 +49,6 @@
|
|||
#include "jsvalue.h"
|
||||
#include "jscell.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) which, in view of subtyping,
|
||||
* would require slower (isX() || isY() || isZ()).
|
||||
*
|
||||
* The string type encoding can be summarized as follows. The "instance
|
||||
* encoding" entry for a type specifies the flag bits used to create a
|
||||
* string instance of that type. Abstract types have no instances and thus
|
||||
* have no such entry. The "subtype predicate" entry for a type specifies
|
||||
* the predicate used to query whether a JSString instance is subtype
|
||||
* (reflexively) of that type.
|
||||
*
|
||||
* string instance subtype
|
||||
* type encoding predicate
|
||||
*
|
||||
* String - true
|
||||
* Rope 0001 xxx1
|
||||
* Linear - xxx0
|
||||
* Dependent 0010 xx1x
|
||||
* Flat - xx00
|
||||
* Extensible 1100 1100
|
||||
* Fixed 0100 isFlat && !isExtensible
|
||||
* Inline 0100 isFixed && (u1.chars == inlineStorage || isShort)
|
||||
* Short 0100 xxxx && header in FINALIZE_SHORT_STRING arena
|
||||
* External 0100 xxxx && header in FINALIZE_EXTERNAL_STRING arena
|
||||
* Atom 1000 x000
|
||||
* InlineAtom 1000 1000 && is Inline
|
||||
* ShortAtom 1000 1000 && is Short
|
||||
* StaticAtom 0000 0000
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* For hot code, prefer other type queries. */
|
||||
bool isShort() const;
|
||||
bool isFixed() const;
|
||||
bool isInline() const;
|
||||
|
||||
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);
|
||||
|
||||
/* Gets the number of bytes that the chars take on the heap. */
|
||||
|
||||
JS_FRIEND_API(size_t) charsHeapSize();
|
||||
|
||||
/* 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;
|
||||
|
||||
public:
|
||||
void mark(JSTracer *trc);
|
||||
|
||||
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 state, this operation
|
||||
* changes the type (in place, as reflected by the flag bits) of the
|
||||
* JSFixedString into a JSAtom.
|
||||
*/
|
||||
inline JSAtom *morphAtomizedStringIntoAtom();
|
||||
};
|
||||
|
||||
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 Cell::CellSize. */
|
||||
static const size_t INLINE_EXTENSION_CHARS = sizeof(JSString::Data) / sizeof(jschar);
|
||||
|
||||
static void staticAsserts() {
|
||||
JS_STATIC_ASSERT(INLINE_EXTENSION_CHARS % js::gc::Cell::CellSize == 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();
|
||||
}
|
||||
|
||||
namespace js {
|
||||
|
||||
/* Implemented in jsstrinlines.h */
|
||||
|
|
|
@ -79,7 +79,9 @@ CheckStringLength(JSContext *cx, size_t length)
|
|||
*/
|
||||
class StringBuffer
|
||||
{
|
||||
typedef Vector<jschar, 32> CharBuffer;
|
||||
/* cb's buffer is taken by the new string so use ContextAllocPolicy. */
|
||||
typedef Vector<jschar, 32, ContextAllocPolicy> CharBuffer;
|
||||
|
||||
CharBuffer cb;
|
||||
|
||||
static inline bool checkLength(JSContext *cx, size_t length);
|
||||
|
@ -255,329 +257,6 @@ 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);
|
||||
|
||||
#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::morphAtomizedStringIntoAtom()
|
||||
{
|
||||
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());
|
||||
|
||||
/*
|
||||
* 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<jschar *>(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;
|
||||
|
|
|
@ -98,6 +98,8 @@
|
|||
#include "methodjit/MethodJIT.h"
|
||||
#endif
|
||||
|
||||
#include "tracejit/Writer-inl.h"
|
||||
|
||||
#include "jsautooplen.h" // generated headers last
|
||||
#include "imacros.c.out"
|
||||
|
||||
|
@ -10148,7 +10150,7 @@ TraceRecorder::guardNativeConversion(Value& v)
|
|||
LIns* obj_ins = get(&v);
|
||||
|
||||
ConvertOp convert = obj->getClass()->convert;
|
||||
if (convert != Valueify(JS_ConvertStub) && convert != js_TryValueOf)
|
||||
if (convert != ConvertStub)
|
||||
RETURN_STOP("operand has convert hook");
|
||||
|
||||
VMSideExit* exit = snapshot(BRANCH_EXIT);
|
||||
|
@ -11415,13 +11417,16 @@ TraceRecorder::callNative(uintN argc, JSOp mode)
|
|||
*/
|
||||
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. */
|
||||
/* 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();
|
||||
Value pval;
|
||||
jsid id = ATOM_TO_JSID(cx->runtime->atomState.testAtom);
|
||||
if (HasDataProperty(proto, id, &pval) &&
|
||||
IsNativeFunction(pval, js_regexp_test))
|
||||
{
|
||||
vp[0] = pval;
|
||||
funobj = &pval.toObject();
|
||||
fun = funobj->getFunctionPrivate();
|
||||
native = js_regexp_test;
|
||||
}
|
||||
}
|
||||
|
@ -15579,13 +15584,18 @@ TraceRecorder::record_JSOP_SETLOCALPOP()
|
|||
}
|
||||
|
||||
JS_REQUIRES_STACK AbortableRecordingStatus
|
||||
TraceRecorder::record_JSOP_IFPRIMTOP()
|
||||
TraceRecorder::record_JSOP_IFCANTCALLTOP()
|
||||
{
|
||||
Value &top = stackval(-1);
|
||||
|
||||
// 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;
|
||||
// nothing if the trace type will be consistently callable or not callable.
|
||||
if (top.isPrimitive() || top.toObject().isFunction())
|
||||
return ARECORD_CONTINUE;
|
||||
|
||||
// Callable objects that aren't also functions would require a guard, but
|
||||
// they're rare, so err on the side of simplicity.
|
||||
return ARECORD_STOP;
|
||||
}
|
||||
|
||||
JS_REQUIRES_STACK AbortableRecordingStatus
|
||||
|
@ -15634,20 +15644,6 @@ TraceRecorder::record_JSOP_ARGSUB()
|
|||
RETURN_STOP_A("can't trace JSOP_ARGSUB hard case");
|
||||
}
|
||||
|
||||
namespace tjit {
|
||||
|
||||
nj::LIns *
|
||||
Writer::getArgsLength(nj::LIns *args) const
|
||||
{
|
||||
uint32 slot = js::ArgumentsObject::INITIAL_LENGTH_SLOT;
|
||||
nj::LIns *vaddr_ins = ldpObjSlots(args);
|
||||
return name(lir->insLoad(nj::LIR_ldi, vaddr_ins, slot * sizeof(Value) + sPayloadOffset,
|
||||
ACCSET_SLOTS),
|
||||
"argsLength");
|
||||
}
|
||||
|
||||
} // namespace tjit
|
||||
|
||||
JS_REQUIRES_STACK LIns*
|
||||
TraceRecorder::guardArgsLengthNotAssigned(LIns* argsobj_ins)
|
||||
{
|
||||
|
|
|
@ -63,10 +63,14 @@
|
|||
#include "jstypedarray.h"
|
||||
|
||||
#include "jsobjinlines.h"
|
||||
#include "jstypedarrayinlines.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::gc;
|
||||
|
||||
/* slots can only be upto 255 */
|
||||
static const uint8 ARRAYBUFFER_RESERVED_SLOTS = 16;
|
||||
|
||||
static bool
|
||||
ValueIsLength(JSContext *cx, const Value &v, jsuint *len)
|
||||
{
|
||||
|
@ -101,33 +105,22 @@ 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)
|
||||
JSObject *
|
||||
ArrayBuffer::getArrayBuffer(JSObject *obj)
|
||||
{
|
||||
while (!js_IsArrayBuffer(obj))
|
||||
obj = obj->getProto();
|
||||
return reinterpret_cast<ArrayBuffer*>(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);
|
||||
vp->setInt32(jsint(ArrayBuffer::getByteLength(arrayBuffer)));
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
ArrayBuffer::class_finalize(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
ArrayBuffer *abuf = ArrayBuffer::fromJSObject(obj);
|
||||
if (abuf) {
|
||||
abuf->freeStorage(cx);
|
||||
cx->delete_(abuf);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* new ArrayBuffer(byteLength)
|
||||
*/
|
||||
|
@ -145,10 +138,36 @@ ArrayBuffer::class_constructor(JSContext *cx, uintN argc, Value *vp)
|
|||
return true;
|
||||
}
|
||||
|
||||
static inline JSBool
|
||||
AllocateSlots(JSContext *cx, JSObject *obj, uint32 size)
|
||||
{
|
||||
uint32 bytes = size + sizeof(js::Value);
|
||||
if (size > sizeof(js::Value) * ARRAYBUFFER_RESERVED_SLOTS - sizeof(js::Value) ) {
|
||||
obj->slots = (js::Value *)cx->calloc_(bytes);
|
||||
if (!obj->slots)
|
||||
return false;
|
||||
} else {
|
||||
memset(obj->slots, 0, bytes);
|
||||
}
|
||||
*((uint32*)obj->slots) = size;
|
||||
return true;
|
||||
}
|
||||
|
||||
static JSObject *
|
||||
DelegateObject(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
if (!obj->getPrivate()) {
|
||||
JSObject *delegate = NewNonFunction<WithProto::Class>(cx, &js_ObjectClass, NULL, NULL);
|
||||
obj->setPrivate(delegate);
|
||||
return delegate;
|
||||
}
|
||||
return static_cast<JSObject*>(obj->getPrivate());
|
||||
}
|
||||
|
||||
JSObject *
|
||||
ArrayBuffer::create(JSContext *cx, int32 nbytes)
|
||||
{
|
||||
JSObject *obj = NewBuiltinClassInstance(cx, &ArrayBuffer::jsclass);
|
||||
JSObject *obj = NewBuiltinClassInstance(cx, &ArrayBuffer::slowClass);
|
||||
if (!obj)
|
||||
return NULL;
|
||||
|
||||
|
@ -162,49 +181,164 @@ ArrayBuffer::create(JSContext *cx, int32 nbytes)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
ArrayBuffer *abuf = cx->new_<ArrayBuffer>();
|
||||
if (!abuf)
|
||||
/*
|
||||
* The first 8 bytes hold the length.
|
||||
* The rest of it is a flat data store for the array buffer.
|
||||
*/
|
||||
if (!AllocateSlots(cx, obj, nbytes))
|
||||
return NULL;
|
||||
|
||||
if (!abuf->allocateStorage(cx, nbytes)) {
|
||||
Foreground::delete_(abuf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
obj->setPrivate(abuf);
|
||||
JS_ASSERT(obj->getClass() == &ArrayBuffer::slowClass);
|
||||
obj->setSharedNonNativeMap();
|
||||
obj->clasp = &ArrayBuffer::fastClass;
|
||||
return obj;
|
||||
}
|
||||
|
||||
bool
|
||||
ArrayBuffer::allocateStorage(JSContext *cx, uint32 nbytes)
|
||||
{
|
||||
JS_ASSERT(data == 0);
|
||||
|
||||
if (nbytes) {
|
||||
data = cx->calloc_(nbytes);
|
||||
if (!data)
|
||||
return false;
|
||||
}
|
||||
|
||||
byteLength = nbytes;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
ArrayBuffer::freeStorage(JSContext *cx)
|
||||
{
|
||||
if (data) {
|
||||
cx->free_(data);
|
||||
#ifdef DEBUG
|
||||
// the destructor asserts that data is 0 in debug builds
|
||||
data = NULL;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
ArrayBuffer::~ArrayBuffer()
|
||||
{
|
||||
JS_ASSERT(data == NULL);
|
||||
}
|
||||
|
||||
void
|
||||
ArrayBuffer::obj_trace(JSTracer *trc, JSObject *obj)
|
||||
{
|
||||
JSObject *delegate = static_cast<JSObject*>(obj->getPrivate());
|
||||
if (delegate)
|
||||
MarkObject(trc, *delegate, "arraybuffer.delegate");
|
||||
}
|
||||
|
||||
JSBool
|
||||
ArrayBuffer::obj_lookupProperty(JSContext *cx, JSObject *obj, jsid id,
|
||||
JSObject **objp, JSProperty **propp)
|
||||
{
|
||||
if (JSID_IS_ATOM(id, cx->runtime->atomState.byteLengthAtom)) {
|
||||
*propp = (JSProperty *) 1;
|
||||
*objp = getArrayBuffer(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
JSObject *delegate = DelegateObject(cx, obj);
|
||||
if (!delegate)
|
||||
return false;
|
||||
|
||||
JSBool delegateResult = delegate->lookupProperty(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)
|
||||
return true;
|
||||
|
||||
JSObject *proto = obj->getProto();
|
||||
if (!proto) {
|
||||
*objp = NULL;
|
||||
*propp = NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
return proto->lookupProperty(cx, id, objp, propp);
|
||||
}
|
||||
|
||||
JSBool
|
||||
ArrayBuffer::obj_defineProperty(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_getProperty(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Value *vp)
|
||||
{
|
||||
obj = getArrayBuffer(obj);
|
||||
if (JSID_IS_ATOM(id, cx->runtime->atomState.byteLengthAtom)) {
|
||||
vp->setInt32(getByteLength(obj));
|
||||
return true;
|
||||
}
|
||||
|
||||
JSObject *delegate = DelegateObject(cx, obj);
|
||||
if (!delegate)
|
||||
return false;
|
||||
return js_GetProperty(cx, delegate, receiver, id, vp);
|
||||
}
|
||||
|
||||
JSBool
|
||||
ArrayBuffer::obj_setProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool strict)
|
||||
{
|
||||
if (JSID_IS_ATOM(id, cx->runtime->atomState.byteLengthAtom))
|
||||
return true;
|
||||
|
||||
JSObject *delegate = DelegateObject(cx, obj);
|
||||
if (!delegate)
|
||||
return false;
|
||||
|
||||
return js_SetProperty(cx, delegate, id, vp, strict);
|
||||
}
|
||||
|
||||
JSBool
|
||||
ArrayBuffer::obj_getAttributes(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_setAttributes(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_deleteProperty(JSContext *cx, JSObject *obj, jsid id, Value *rval, JSBool strict)
|
||||
{
|
||||
if (JSID_IS_ATOM(id, cx->runtime->atomState.byteLengthAtom)) {
|
||||
rval->setBoolean(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
JSObject *delegate = DelegateObject(cx, obj);
|
||||
if (!delegate)
|
||||
return false;
|
||||
return js_DeleteProperty(cx, delegate, id, 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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -979,21 +1113,21 @@ class TypedArrayTemplate
|
|||
public:
|
||||
TypedArrayTemplate(JSObject *bufobj, uint32 byteOffset, uint32 len)
|
||||
{
|
||||
JS_ASSERT(bufobj->getClass() == &ArrayBuffer::jsclass);
|
||||
JS_ASSERT(bufobj->getClass() == &ArrayBuffer::fastClass);
|
||||
|
||||
type = ArrayTypeID();
|
||||
bufferJS = bufobj;
|
||||
buffer = ArrayBuffer::fromJSObject(bufobj);
|
||||
length = 0;
|
||||
|
||||
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));
|
||||
JS_ASSERT(byteOffset <= ArrayBuffer::getByteLength(bufferJS));
|
||||
this->data = offsetData(bufferJS, byteOffset);
|
||||
JS_ASSERT(ArrayBuffer::getDataOffset(bufferJS) <= this->data);
|
||||
JS_ASSERT(this->data <= offsetData(bufferJS, ArrayBuffer::getByteLength(bufferJS)));
|
||||
|
||||
this->byteLength = len * sizeof(NativeType);
|
||||
JS_ASSERT(buffer->byteLength - byteOffset >= this->byteLength);
|
||||
JS_ASSERT(ArrayBuffer::getByteLength(bufferJS) - byteOffset >= this->byteLength);
|
||||
|
||||
this->length = len;
|
||||
}
|
||||
|
@ -1005,12 +1139,10 @@ class TypedArrayTemplate
|
|||
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)) {
|
||||
if (other->getClass() == &ArrayBuffer::fastClass) {
|
||||
uint32 boffset = (byteOffsetInt < 0) ? 0 : uint32(byteOffsetInt);
|
||||
|
||||
if (boffset > abuf->byteLength || boffset % sizeof(NativeType) != 0) {
|
||||
if (boffset > ArrayBuffer::getByteLength(other) || boffset % sizeof(NativeType) != 0) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
||||
JSMSG_TYPED_ARRAY_BAD_ARGS);
|
||||
return NULL; // invalid byteOffset
|
||||
|
@ -1018,8 +1150,8 @@ class TypedArrayTemplate
|
|||
|
||||
uint32 len;
|
||||
if (lengthInt < 0) {
|
||||
len = (abuf->byteLength - boffset) / sizeof(NativeType);
|
||||
if (len * sizeof(NativeType) != (abuf->byteLength - boffset)) {
|
||||
len = (ArrayBuffer::getByteLength(other) - boffset) / sizeof(NativeType);
|
||||
if (len * sizeof(NativeType) != (ArrayBuffer::getByteLength(other) - 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
|
||||
|
@ -1038,7 +1170,7 @@ class TypedArrayTemplate
|
|||
return NULL; // overflow occurred along the way when calculating boffset+len*sizeof(NativeType)
|
||||
}
|
||||
|
||||
if (arrayByteLength + boffset > abuf->byteLength) {
|
||||
if (arrayByteLength + boffset > ArrayBuffer::getByteLength(other)) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
||||
JSMSG_TYPED_ARRAY_BAD_ARGS);
|
||||
return NULL; // boffset+len is too big for the arraybuffer
|
||||
|
@ -1172,7 +1304,7 @@ class TypedArrayTemplate
|
|||
|
||||
JS_ASSERT(offset <= thisTypedArray->length);
|
||||
JS_ASSERT(tarray->length <= thisTypedArray->length - offset);
|
||||
if (tarray->buffer == thisTypedArray->buffer)
|
||||
if (tarray->bufferJS == thisTypedArray->bufferJS)
|
||||
return thisTypedArray->copyFromWithOverlap(cx, tarray, offset);
|
||||
|
||||
NativeType *dest = static_cast<NativeType*>(thisTypedArray->data) + offset;
|
||||
|
@ -1319,6 +1451,11 @@ class TypedArrayTemplate
|
|||
return true;
|
||||
}
|
||||
|
||||
void *
|
||||
offsetData(JSObject *obj, uint32 offs) {
|
||||
return (void*)(((uint8*)ArrayBuffer::getDataOffset(obj)) + offs);
|
||||
}
|
||||
|
||||
static JSObject *
|
||||
createBufferWithSizeAndCount(JSContext *cx, uint32 count)
|
||||
{
|
||||
|
@ -1412,10 +1549,10 @@ TypedArrayTemplate<double>::copyIndexToValue(JSContext *cx, uint32 index, Value
|
|||
* ArrayBuffer (base)
|
||||
*/
|
||||
|
||||
Class ArrayBuffer::jsclass = {
|
||||
Class ArrayBuffer::slowClass = {
|
||||
"ArrayBuffer",
|
||||
JSCLASS_HAS_PRIVATE |
|
||||
JSCLASS_CONCURRENT_FINALIZER |
|
||||
JSCLASS_HAS_RESERVED_SLOTS(ARRAYBUFFER_RESERVED_SLOTS) |
|
||||
JSCLASS_HAS_CACHED_PROTO(JSProto_ArrayBuffer),
|
||||
PropertyStub, /* addProperty */
|
||||
PropertyStub, /* delProperty */
|
||||
|
@ -1424,7 +1561,44 @@ Class ArrayBuffer::jsclass = {
|
|||
EnumerateStub,
|
||||
ResolveStub,
|
||||
ConvertStub,
|
||||
ArrayBuffer::class_finalize,
|
||||
FinalizeStub
|
||||
};
|
||||
|
||||
Class ArrayBuffer::fastClass = {
|
||||
"ArrayBuffer",
|
||||
JSCLASS_HAS_PRIVATE |
|
||||
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,
|
||||
NULL, /* finalize */
|
||||
NULL, /* reserved0 */
|
||||
NULL, /* checkAccess */
|
||||
NULL, /* call */
|
||||
NULL, /* construct */
|
||||
NULL, /* xdrObject */
|
||||
NULL, /* hasInstance */
|
||||
ArrayBuffer::obj_trace,
|
||||
JS_NULL_CLASS_EXT,
|
||||
{
|
||||
ArrayBuffer::obj_lookupProperty,
|
||||
ArrayBuffer::obj_defineProperty,
|
||||
ArrayBuffer::obj_getProperty,
|
||||
ArrayBuffer::obj_setProperty,
|
||||
ArrayBuffer::obj_getAttributes,
|
||||
ArrayBuffer::obj_setAttributes,
|
||||
ArrayBuffer::obj_deleteProperty,
|
||||
ArrayBuffer::obj_enumerate,
|
||||
ArrayBuffer::obj_typeOf,
|
||||
NULL, /* thisObject */
|
||||
NULL, /* clear */
|
||||
}
|
||||
};
|
||||
|
||||
JSPropertySpec ArrayBuffer::jsprops[] = {
|
||||
|
@ -1596,13 +1770,21 @@ js_InitTypedArrayClasses(JSContext *cx, JSObject *obj)
|
|||
INIT_TYPED_ARRAY_CLASS(Float64Array,TYPE_FLOAT64);
|
||||
INIT_TYPED_ARRAY_CLASS(Uint8ClampedArray,TYPE_UINT8_CLAMPED);
|
||||
|
||||
proto = js_InitClass(cx, obj, NULL, &ArrayBuffer::jsclass,
|
||||
proto = js_InitClass(cx, obj, NULL, &ArrayBuffer::slowClass,
|
||||
ArrayBuffer::class_constructor, 1,
|
||||
ArrayBuffer::jsprops, NULL, NULL, NULL);
|
||||
if (!proto)
|
||||
return NULL;
|
||||
|
||||
proto->setPrivate(NULL);
|
||||
|
||||
/*
|
||||
* Initialize the slots to hold the length as 0
|
||||
* This is required otherwise the length of a
|
||||
* ArrayBuffer's prototype is undefined.
|
||||
*/
|
||||
if (!AllocateSlots(cx, proto, 0))
|
||||
return NULL;
|
||||
return proto;
|
||||
}
|
||||
|
||||
|
@ -1610,7 +1792,20 @@ JS_FRIEND_API(JSBool)
|
|||
js_IsArrayBuffer(JSObject *obj)
|
||||
{
|
||||
JS_ASSERT(obj);
|
||||
return obj->getClass() == &ArrayBuffer::jsclass;
|
||||
return obj->getClass() == &ArrayBuffer::fastClass;
|
||||
}
|
||||
|
||||
JSUint32
|
||||
JS_GetArrayBufferByteLength(JSObject *obj)
|
||||
{
|
||||
return *((JSUint32*) obj->slots);
|
||||
}
|
||||
|
||||
uint8 *
|
||||
JS_GetArrayBufferData(JSObject *obj)
|
||||
{
|
||||
uint64 *base = ((uint64*)obj->slots) + 1;
|
||||
return (uint8*) base;
|
||||
}
|
||||
|
||||
JS_FRIEND_API(JSBool)
|
||||
|
@ -1688,7 +1883,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];
|
||||
|
|
|
@ -56,34 +56,61 @@ namespace js {
|
|||
* TypedArray with a size.
|
||||
*/
|
||||
struct JS_FRIEND_API(ArrayBuffer) {
|
||||
static Class jsclass;
|
||||
static Class slowClass;
|
||||
static Class fastClass;
|
||||
static JSPropertySpec jsprops[];
|
||||
|
||||
static JSBool prop_getByteLength(JSContext *cx, JSObject *obj, jsid id, Value *vp);
|
||||
static void class_finalize(JSContext *cx, JSObject *obj);
|
||||
|
||||
static JSBool class_constructor(JSContext *cx, uintN argc, Value *vp);
|
||||
|
||||
static JSObject *create(JSContext *cx, int32 nbytes);
|
||||
|
||||
static ArrayBuffer *fromJSObject(JSObject *obj);
|
||||
|
||||
ArrayBuffer()
|
||||
: data(0), byteLength()
|
||||
{
|
||||
}
|
||||
|
||||
~ArrayBuffer();
|
||||
|
||||
bool allocateStorage(JSContext *cx, uint32 bytes);
|
||||
void freeStorage(JSContext *cx);
|
||||
static void
|
||||
obj_trace(JSTracer *trc, JSObject *obj);
|
||||
|
||||
void *offsetData(uint32 offs) {
|
||||
return (void*) (((intptr_t)data) + offs);
|
||||
}
|
||||
static JSBool
|
||||
obj_lookupProperty(JSContext *cx, JSObject *obj, jsid id,
|
||||
JSObject **objp, JSProperty **propp);
|
||||
|
||||
void *data;
|
||||
uint32 byteLength;
|
||||
static JSBool
|
||||
obj_defineProperty(JSContext *cx, JSObject *obj, jsid id, const Value *v,
|
||||
PropertyOp getter, StrictPropertyOp setter, uintN attrs);
|
||||
|
||||
static JSBool
|
||||
obj_getProperty(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Value *vp);
|
||||
|
||||
static JSBool
|
||||
obj_setProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool strict);
|
||||
|
||||
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 JSBool
|
||||
obj_deleteProperty(JSContext *cx, JSObject *obj, jsid id, 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);
|
||||
|
||||
static inline unsigned int getByteLength(JSObject *obj);
|
||||
|
||||
static inline uint8 * getDataOffset(JSObject *obj);
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -142,14 +169,14 @@ struct JS_FRIEND_API(TypedArray) {
|
|||
static int32 lengthOffset() { return offsetof(TypedArray, length); }
|
||||
static int32 dataOffset() { return offsetof(TypedArray, data); }
|
||||
static int32 typeOffset() { return offsetof(TypedArray, type); }
|
||||
static void *offsetData(JSObject *obj, uint32 offs);
|
||||
|
||||
public:
|
||||
TypedArray() : buffer(0) { }
|
||||
TypedArray() : bufferJS(0) { }
|
||||
|
||||
bool isArrayIndex(JSContext *cx, jsid id, jsuint *ip = NULL);
|
||||
bool valid() { return buffer != 0; }
|
||||
bool valid() { return bufferJS != 0; }
|
||||
|
||||
ArrayBuffer *buffer;
|
||||
JSObject *bufferJS;
|
||||
uint32 byteOffset;
|
||||
uint32 byteLength;
|
||||
|
@ -225,4 +252,10 @@ js_CreateTypedArrayWithBuffer(JSContext *cx, jsint atype, JSObject *bufArg,
|
|||
extern int32 JS_FASTCALL
|
||||
js_TypedArray_uint8_clamp_double(const double x);
|
||||
|
||||
JS_FRIEND_API(JSUint32)
|
||||
JS_GetArrayBufferByteLength(JSObject *obj);
|
||||
|
||||
JS_FRIEND_API(uint8 *)
|
||||
JS_GetArrayBufferData(JSObject *obj);
|
||||
|
||||
#endif /* jstypedarray_h */
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче