Merge mozilla-central into mozilla-inbound

This commit is contained in:
Ehsan Akhgari 2011-06-20 23:42:45 -04:00
Родитель 9a38106463 6ad07be206
Коммит 92ab9f79c9
198 изменённых файлов: 6152 добавлений и 4068 удалений

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

@ -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;

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

@ -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)
{

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

@ -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,

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

@ -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, &regs.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);

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

@ -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)

119
js/src/jsnuminlines.h Normal file
Просмотреть файл

@ -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___ */

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

@ -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(&quot, &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);

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

@ -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())

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

@ -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 */

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