--- a/js/src/assembler/assembler/ARMAssembler.h
+++ b/js/src/assembler/assembler/ARMAssembler.h
@@ -942,22 +942,20 @@ namespace JSC {
m_buffer.ensureSpace(space);
}
int sizeOfConstantPool()
{
return m_buffer.sizeOfConstantPool();
}
-#ifdef DEBUG
- void allowPoolFlush(bool allowFlush)
+ int flushCount()
{
- m_buffer.allowPoolFlush(allowFlush);
+ return m_buffer.flushCount();
}
-#endif
JmpDst label()
{
JmpDst label(m_buffer.size());
js::JaegerSpew(js::JSpew_Insns, IPFX "#label ((%d))\n", MAYBE_PAD, label.m_offset);
return label;
}
--- a/js/src/assembler/assembler/AssemblerBufferWithConstantPool.h
+++ b/js/src/assembler/assembler/AssemblerBufferWithConstantPool.h
@@ -101,19 +101,17 @@ public:
UnusedEntry
};
AssemblerBufferWithConstantPool()
: AssemblerBuffer()
, m_numConsts(0)
, m_maxDistance(maxPoolSize)
, m_lastConstDelta(0)
-#ifdef DEBUG
- , m_allowFlush(true)
-#endif
+ , m_flushCount(0)
{
m_pool = static_cast<uint32_t*>(malloc(maxPoolSize));
m_mask = static_cast<char*>(malloc(maxPoolSize / sizeof(uint32_t)));
}
~AssemblerBufferWithConstantPool()
{
free(m_mask);
@@ -236,24 +234,20 @@ public:
return m_pool;
}
int sizeOfConstantPool()
{
return m_numConsts;
}
-#ifdef DEBUG
- // Guard constant pool flushes to ensure that they don't occur during
- // regions where offsets into the code have to be maintained (such as PICs).
- void allowPoolFlush(bool allowFlush)
+ int flushCount()
{
- m_allowFlush = allowFlush;
+ return m_flushCount;
}
-#endif
private:
void correctDeltas(int insnSize)
{
m_maxDistance -= insnSize;
ASSERT(m_maxDistance >= 0);
m_lastConstDelta -= insnSize;
if (m_lastConstDelta < 0)
@@ -268,19 +262,19 @@ private:
ASSERT(m_maxDistance >= 0);
m_lastConstDelta = constSize;
}
void flushConstantPool(bool useBarrier = true)
{
js::JaegerSpew(js::JSpew_Insns, " -- FLUSHING CONSTANT POOL WITH %d CONSTANTS --\n",
m_numConsts);
- ASSERT(m_allowFlush);
if (m_numConsts == 0)
return;
+ m_flushCount++;
int alignPool = (AssemblerBuffer::size() + (useBarrier ? barrierSize : 0)) & (sizeof(uint64_t) - 1);
if (alignPool)
alignPool = sizeof(uint64_t) - alignPool;
// Callback to protect the constant pool from execution
if (useBarrier)
AssemblerBuffer::putInt(AssemblerType::placeConstantPoolBarrier(m_numConsts * sizeof(uint32_t) + alignPool));
@@ -334,19 +328,16 @@ private:
uint32_t* m_pool;
char* m_mask;
LoadOffsets m_loadOffsets;
int m_numConsts;
int m_maxDistance;
int m_lastConstDelta;
-
-#ifdef DEBUG
- bool m_allowFlush;
-#endif
+ int m_flushCount;
};
} // namespace JSC
#endif // ENABLE(ASSEMBLER)
#endif // AssemblerBufferWithConstantPool_h
--- a/js/src/assembler/assembler/MacroAssemblerARM.h
+++ b/js/src/assembler/assembler/MacroAssemblerARM.h
@@ -1375,22 +1375,20 @@ public:
m_assembler.ensureSpace(space);
}
void forceFlushConstantPool()
{
m_assembler.forceFlushConstantPool();
}
-#ifdef DEBUG
- void allowPoolFlush(bool allowFlush)
+ int flushCount()
{
- m_assembler.allowPoolFlush(allowFlush);
+ return m_assembler.flushCount();
}
-#endif
protected:
ARMAssembler::Condition ARMCondition(Condition cond)
{
return static_cast<ARMAssembler::Condition>(cond);
}
void ensureSpace(int insnSpace, int constSpace)
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug683140.js
@@ -0,0 +1,13 @@
+
+var g = newGlobal("same-compartment");
+g.eval("this.f = function(a) {" +
+ "assertEq(a instanceof Array, false);" +
+ "a = Array.prototype.slice.call(a);" +
+ "assertEq(a instanceof Array, true); }");
+g.f([1, 2, 3]);
+
+var g2 = newGlobal("new-compartment");
+g2.a = g2.Array(10);
+assertEq(g2.a instanceof Array, false);
+g2.a = Array.prototype.slice(g2.a);
+assertEq(g2.a instanceof Array, true);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug686396.js
@@ -0,0 +1,16 @@
+
+(function () {
+ assertEquals = function assertEquals(expected, found, name_opt) { };
+})();
+function testOne(receiver, key, result) {
+ for(var i = 0; i != 10; i++ ) {
+ assertEquals(result, receiver[key]());
+ }
+}
+function TypeOfThis() { return typeof this; }
+Number.prototype.type = TypeOfThis;
+String.prototype.type = TypeOfThis;
+Boolean.prototype.type = TypeOfThis;
+testOne(2.3, 'type', 'object');
+testOne('x', 'type', 'object');
+testOne(true, 'type', 'object');
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/jaeger/bug684084-2.js
@@ -0,0 +1,8 @@
+function Function() {
+ try {
+ var g = this;
+ g.c("evil", eval);
+ } catch(b) {}
+}
+var o0 = Function.prototype;
+var f = new Function( (null ) );
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/jaeger/bug684824.js
@@ -0,0 +1,7 @@
+
+function X(n) {
+ while ('' + (n--)) {
+ break;
+ }
+}
+X();
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -2610,44 +2610,46 @@ array_unshift(JSContext *cx, uintN argc,
if (!js_SetLengthProperty(cx, obj, newlen))
return JS_FALSE;
/* Follow Perl by returning the new array length. */
vp->setNumber(newlen);
return JS_TRUE;
}
+static inline void
+TryReuseArrayType(JSObject *obj, JSObject *nobj)
+{
+ /*
+ * Try to change the type of a newly created array nobj to the same type
+ * as obj. This can only be performed if the original object is an array
+ * and has the same prototype.
+ */
+ JS_ASSERT(nobj->isDenseArray());
+ JS_ASSERT(nobj->type() == nobj->getProto()->newType);
+
+ if (obj->isArray() && !obj->hasSingletonType() && obj->getProto() == nobj->getProto())
+ nobj->setType(obj->type());
+}
+
static JSBool
array_splice(JSContext *cx, uintN argc, Value *vp)
{
JSObject *obj = ToObject(cx, &vp[1]);
if (!obj)
return false;
jsuint length, begin, end, count, delta, last;
JSBool hole;
- /*
- * Get the type of the result object: the original type when splicing an
- * array, a generic array type otherwise.
- */
- TypeObject *type;
- if (obj->isArray() && !obj->hasSingletonType()) {
- type = obj->type();
- } else {
- type = GetTypeNewObject(cx, JSProto_Array);
- if (!type)
- return false;
- }
-
/* Create a new array value to return. */
JSObject *obj2 = NewDenseEmptyArray(cx);
if (!obj2)
return JS_FALSE;
- obj2->setType(type);
+ TryReuseArrayType(obj, obj2);
vp->setObject(*obj2);
/* Nothing to do if no args. Otherwise get length. */
if (argc == 0)
return JS_TRUE;
Value *argv = JS_ARGV(cx, vp);
if (!js_GetLengthProperty(cx, obj, &length))
return JS_FALSE;
@@ -2796,18 +2798,17 @@ array_concat(JSContext *cx, uintN argc,
jsuint length;
if (aobj->isDenseArray()) {
length = aobj->getArrayLength();
const Value *vector = aobj->getDenseArrayElements();
jsuint initlen = aobj->getDenseArrayInitializedLength();
nobj = NewDenseCopiedArray(cx, initlen, vector);
if (!nobj)
return JS_FALSE;
- if (nobj->getProto() == aobj->getProto() && !aobj->hasSingletonType())
- nobj->setType(aobj->type());
+ TryReuseArrayType(aobj, nobj);
nobj->setArrayLength(cx, length);
if (!aobj->isPackedDenseArray())
nobj->markDenseArrayNotPacked(cx);
vp->setObject(*nobj);
if (argc == 0)
return JS_TRUE;
argc--;
p++;
@@ -2900,43 +2901,33 @@ array_slice(JSContext *cx, uintN argc, V
}
end = (jsuint)d;
}
}
if (begin > end)
begin = end;
- /* Get the type object for the returned array, as for array_splice. */
- TypeObject *type;
- if (obj->isArray() && !obj->hasSingletonType()) {
- type = obj->type();
- } else {
- type = GetTypeNewObject(cx, JSProto_Array);
- if (!type)
- return false;
- }
-
if (obj->isDenseArray() && end <= obj->getDenseArrayInitializedLength() &&
!js_PrototypeHasIndexedProperties(cx, obj)) {
nobj = NewDenseCopiedArray(cx, end - begin, obj->getDenseArrayElements() + begin);
if (!nobj)
return JS_FALSE;
- nobj->setType(type);
+ TryReuseArrayType(obj, nobj);
if (!obj->isPackedDenseArray())
nobj->markDenseArrayNotPacked(cx);
vp->setObject(*nobj);
return JS_TRUE;
}
/* Create a new Array object and root it using *vp. */
nobj = NewDenseAllocatedArray(cx, end - begin);
if (!nobj)
return JS_FALSE;
- nobj->setType(type);
+ TryReuseArrayType(obj, nobj);
vp->setObject(*nobj);
AutoValueRooter tvr(cx);
for (slot = begin; slot < end; slot++) {
if (!JS_CHECK_OPERATION_LIMIT(cx) ||
!GetElement(cx, obj, slot, &hole, tvr.addr())) {
return JS_FALSE;
}
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -686,34 +686,35 @@ TypeSet::addTransformThis(JSContext *cx,
* discovered scripted functions.
*/
class TypeConstraintPropagateThis : public TypeConstraint
{
public:
JSScript *script;
jsbytecode *callpc;
Type type;
-
- TypeConstraintPropagateThis(JSScript *script, jsbytecode *callpc, Type type)
- : TypeConstraint("propagatethis"), script(script), callpc(callpc), type(type)
+ TypeSet *types;
+
+ TypeConstraintPropagateThis(JSScript *script, jsbytecode *callpc, Type type, TypeSet *types)
+ : TypeConstraint("propagatethis"), script(script), callpc(callpc), type(type), types(types)
{}
void newType(JSContext *cx, TypeSet *source, Type type);
};
void
-TypeSet::addPropagateThis(JSContext *cx, JSScript *script, jsbytecode *pc, Type type)
+TypeSet::addPropagateThis(JSContext *cx, JSScript *script, jsbytecode *pc, Type type, TypeSet *types)
{
/* Don't add constraints when the call will be 'new' (see addCallProperty). */
jsbytecode *callpc = script->analysis()->getCallPC(pc);
UntrapOpcode untrap(cx, script, callpc);
if (JSOp(*callpc) == JSOP_NEW)
return;
- add(cx, ArenaNew<TypeConstraintPropagateThis>(cx->compartment->pool, script, callpc, type));
+ add(cx, ArenaNew<TypeConstraintPropagateThis>(cx->compartment->pool, script, callpc, type, types));
}
/* Subset constraint which filters out primitive types. */
class TypeConstraintFilterPrimitive : public TypeConstraint
{
public:
TypeSet *target;
TypeSet::FilterKind filter;
@@ -1058,20 +1059,20 @@ TypeConstraintProp::newType(JSContext *c
}
void
TypeConstraintCallProp::newType(JSContext *cx, TypeSet *source, Type type)
{
UntrapOpcode untrap(cx, script, callpc);
/*
- * For CALLPROP and CALLELEM, we need to update not just the pushed types
- * but also the 'this' types of possible callees. If we can't figure out
- * that set of callees, monitor the call to make sure discovered callees
- * get their 'this' types updated.
+ * For CALLPROP, we need to update not just the pushed types but also the
+ * 'this' types of possible callees. If we can't figure out that set of
+ * callees, monitor the call to make sure discovered callees get their
+ * 'this' types updated.
*/
if (UnknownPropertyAccess(script, type)) {
cx->compartment->types.monitorBytecode(cx, script, callpc - script->code);
return;
}
TypeObject *object = GetPropertyObject(cx, script, type);
@@ -1081,17 +1082,18 @@ TypeConstraintCallProp::newType(JSContex
} else {
TypeSet *types = object->getProperty(cx, id, false);
if (!types)
return;
if (!types->hasPropagatedProperty())
object->getFromPrototypes(cx, id, types);
/* Bypass addPropagateThis, we already have the callpc. */
types->add(cx, ArenaNew<TypeConstraintPropagateThis>(cx->compartment->pool,
- script, callpc, type));
+ script, callpc, type,
+ (TypeSet *) NULL));
}
}
}
void
TypeConstraintSetElement::newType(JSContext *cx, TypeSet *source, Type type)
{
if (type.isUnknown() ||
@@ -1225,18 +1227,18 @@ TypeConstraintCall::newType(JSContext *c
void
TypeConstraintPropagateThis::newType(JSContext *cx, TypeSet *source, Type type)
{
if (type.isUnknown() || type.isAnyObject()) {
/*
* The callee is unknown, make sure the call is monitored so we pick up
* possible this/callee correlations. This only comes into play for
- * CALLPROP and CALLELEM, for other calls we are past the type barrier
- * already and a TypeConstraintCall will also monitor the call.
+ * CALLPROP, for other calls we are past the type barrier and a
+ * TypeConstraintCall will also monitor the call.
*/
cx->compartment->types.monitorBytecode(cx, script, callpc - script->code);
return;
}
/* Ignore calls to natives, these will be handled by TypeConstraintCall. */
JSFunction *callee = NULL;
@@ -1253,17 +1255,21 @@ TypeConstraintPropagateThis::newType(JSC
} else {
/* Ignore calls to primitives, these will go through a stub. */
return;
}
if (!callee->script()->ensureHasTypes(cx, callee))
return;
- TypeScript::ThisTypes(callee->script())->addType(cx, this->type);
+ TypeSet *thisTypes = TypeScript::ThisTypes(callee->script());
+ if (this->types)
+ this->types->addSubset(cx, thisTypes);
+ else
+ thisTypes->addType(cx, this->type);
}
void
TypeConstraintArith::newType(JSContext *cx, TypeSet *source, Type type)
{
/*
* We only model a subset of the arithmetic behavior that is actually
* possible. The following need to be watched for at runtime:
@@ -3677,22 +3683,22 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
* which is accessing a non-integer property must be monitored.
*/
case JSOP_GETELEM:
case JSOP_CALLELEM: {
TypeSet *seen = script->analysis()->bytecodeTypes(pc);
poppedTypes(pc, 1)->addGetProperty(cx, script, pc, seen, JSID_VOID);
- if (op == JSOP_CALLELEM)
- poppedTypes(pc, 1)->addCallProperty(cx, script, pc, JSID_VOID);
seen->addSubset(cx, &pushed[0]);
- if (op == JSOP_CALLELEM)
+ if (op == JSOP_CALLELEM) {
poppedTypes(pc, 1)->addFilterPrimitives(cx, &pushed[1], TypeSet::FILTER_NULL_VOID);
+ pushed[0].addPropagateThis(cx, script, pc, Type::UndefinedType(), &pushed[1]);
+ }
if (CheckNextTest(pc))
pushed[0].addType(cx, Type::UndefinedType());
break;
}
case JSOP_SETELEM:
poppedTypes(pc, 1)->addSetElement(cx, script, pc, poppedTypes(pc, 2), poppedTypes(pc, 0));
poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -447,17 +447,18 @@ class TypeSet
void addSetProperty(JSContext *cx, JSScript *script, jsbytecode *pc,
TypeSet *target, jsid id);
void addCallProperty(JSContext *cx, JSScript *script, jsbytecode *pc, jsid id);
void addSetElement(JSContext *cx, JSScript *script, jsbytecode *pc,
TypeSet *objectTypes, TypeSet *valueTypes);
void addCall(JSContext *cx, TypeCallsite *site);
void addArith(JSContext *cx, TypeSet *target, TypeSet *other = NULL);
void addTransformThis(JSContext *cx, JSScript *script, TypeSet *target);
- void addPropagateThis(JSContext *cx, JSScript *script, jsbytecode *pc, Type type);
+ void addPropagateThis(JSContext *cx, JSScript *script, jsbytecode *pc,
+ Type type, TypeSet *types = NULL);
void addFilterPrimitives(JSContext *cx, TypeSet *target, FilterKind filter);
void addSubsetBarrier(JSContext *cx, JSScript *script, jsbytecode *pc, TypeSet *target);
void addLazyArguments(JSContext *cx, TypeSet *target);
/*
* Make an type set with the specified debugging name, not embedded in
* another structure.
*/
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -5683,16 +5683,18 @@ js_NativeGetInline(JSContext *cx, JSObje
int32 sample;
JS_ASSERT(pobj->isNative());
slot = shape->slot;
if (slot != SHAPE_INVALID_SLOT) {
*vp = pobj->nativeGetSlot(slot);
JS_ASSERT(!vp->isMagic());
+ JS_ASSERT_IF(!pobj->hasSingletonType() && shape->hasDefaultGetterOrIsMethod(),
+ js::types::TypeHasProperty(cx, pobj->type(), shape->propid, *vp));
} else {
vp->setUndefined();
}
if (shape->hasDefaultGetter())
return true;
if (JS_UNLIKELY(shape->isMethod()) && (getHow & JSGET_NO_METHOD_BARRIER)) {
JS_ASSERT(shape->methodObject() == vp->toObject());
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -139,18 +139,16 @@ JSObject::getProperty(JSContext *cx, JSO
{
js::PropertyIdOp op = getOps()->getProperty;
if (op) {
if (!op(cx, this, receiver, id, vp))
return false;
} else {
if (!js_GetProperty(cx, this, receiver, id, vp))
return false;
- JS_ASSERT_IF(!hasSingletonType() && nativeContains(cx, js_CheckForStringIndex(id)),
- js::types::TypeHasProperty(cx, type(), id, *vp));
}
return true;
}
inline JSBool
JSObject::getProperty(JSContext *cx, jsid id, js::Value *vp)
{
return getProperty(cx, this, id, vp);
--- a/js/src/methodjit/BaseCompiler.h
+++ b/js/src/methodjit/BaseCompiler.h
@@ -222,77 +222,53 @@ NativeStubEpilogue(VMFrame &f, Assembler
* up front to prevent this from happening.
*/
#ifdef JS_CPU_ARM
template <size_t reservedSpace>
class AutoReserveICSpace {
typedef Assembler::Label Label;
Assembler &masm;
-#ifdef DEBUG
- Label startLabel;
bool didCheck;
-#endif
+ bool *overflowSpace;
+ int flushCount;
public:
- AutoReserveICSpace(Assembler &masm) : masm(masm) {
+ AutoReserveICSpace(Assembler &masm, bool *overflowSpace)
+ : masm(masm), didCheck(false), overflowSpace(overflowSpace)
+ {
masm.ensureSpace(reservedSpace);
-#ifdef DEBUG
- didCheck = false;
-
- startLabel = masm.label();
-
- /* Assert that the constant pool is not flushed until we reach a safe point. */
- masm.allowPoolFlush(false);
-
- JaegerSpew(JSpew_Insns, " -- BEGIN CONSTANT-POOL-FREE REGION -- \n");
-#endif
+ flushCount = masm.flushCount();
}
/* Allow manual IC space checks so that non-patchable code at the end of an IC section can be
* free to use constant pools. */
void check() {
-#ifdef DEBUG
JS_ASSERT(!didCheck);
didCheck = true;
- Label endLabel = masm.label();
- int spaceUsed = masm.differenceBetween(startLabel, endLabel);
-
- /* Spew the space used, to help tuning of reservedSpace. */
- JaegerSpew(JSpew_Insns,
- " -- END CONSTANT-POOL-FREE REGION: %u bytes used of %u reserved. -- \n",
- spaceUsed, reservedSpace);
-
- /* Assert that we didn't emit more code than we protected. */
- JS_ASSERT(spaceUsed >= 0);
- JS_ASSERT(size_t(spaceUsed) <= reservedSpace);
-
- /* Allow the pool to be flushed. */
- masm.allowPoolFlush(true);
-#endif
+ if (masm.flushCount() != flushCount)
+ *overflowSpace = true;
}
~AutoReserveICSpace() {
-#ifdef DEBUG
/* Automatically check the IC space if we didn't already do it manually. */
if (!didCheck) {
check();
}
-#endif
}
};
-# define RESERVE_IC_SPACE(__masm) AutoReserveICSpace<256> arics(__masm)
+# define RESERVE_IC_SPACE(__masm) AutoReserveICSpace<256> arics(__masm, &this->overflowICSpace)
# define CHECK_IC_SPACE() arics.check()
/* The OOL path can need a lot of space because we save and restore a lot of registers. The actual
* sequene varies. However, dumping the literal pool before an OOL block is probably a good idea
* anyway, as we branch directly to the start of the block from the fast path. */
-# define RESERVE_OOL_SPACE(__masm) AutoReserveICSpace<2048> arics_ool(__masm)
+# define RESERVE_OOL_SPACE(__masm) AutoReserveICSpace<2048> arics_ool(__masm, &this->overflowICSpace)
/* Allow the OOL patch to be checked before object destruction. Often, non-patchable epilogues or
* rejoining sequences are emitted, and it isn't necessary to protect these from literal pools. */
# define CHECK_OOL_SPACE() arics_ool.check()
#else
# define RESERVE_IC_SPACE(__masm) /* Do nothing. */
# define CHECK_IC_SPACE() /* Do nothing. */
# define RESERVE_OOL_SPACE(__masm) /* Do nothing. */
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -128,16 +128,17 @@ mjit::Compiler::Compiler(JSContext *cx,
#if defined JS_TRACER
addTraceHints(cx->traceJitEnabled),
#else
addTraceHints(false),
#endif
inlining_(false),
hasGlobalReallocation(false),
oomInVector(false),
+ overflowICSpace(false),
gcNumber(cx->runtime->gcNumber),
applyTricks(NoApplyTricks),
pcLengths(NULL)
{
/* :FIXME: bug 637856 disabling traceJit if inference is enabled */
if (cx->typeInferenceEnabled())
addTraceHints = false;
@@ -889,16 +890,21 @@ mjit::Compiler::finishThisUp(JITScript *
/*
* Watch for GCs which occurred during compilation. These may have
* renumbered shapes baked into the jitcode.
*/
if (cx->runtime->gcNumber != gcNumber)
return Compile_Retry;
+ if (overflowICSpace) {
+ JaegerSpew(JSpew_Scripts, "dumped a constant pool while generating an IC\n");
+ return Compile_Abort;
+ }
+
for (size_t i = 0; i < branchPatches.length(); i++) {
Label label = labelOf(branchPatches[i].pc, branchPatches[i].inlineIndex);
branchPatches[i].jump.linkTo(label, &masm);
}
#ifdef JS_CPU_ARM
masm.forceFlushConstantPool();
stubcc.masm.forceFlushConstantPool();
@@ -6892,49 +6898,64 @@ mjit::Compiler::leaveBlock()
//
bool
mjit::Compiler::constructThis()
{
JS_ASSERT(isConstructing);
JSFunction *fun = script->function();
- if (cx->typeInferenceEnabled() && !fun->getType(cx)->unknownProperties()) {
+ do {
+ if (!cx->typeInferenceEnabled() || fun->getType(cx)->unknownProperties())
+ break;
+
jsid id = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom);
types::TypeSet *protoTypes = fun->getType(cx)->getProperty(cx, id, false);
JSObject *proto = protoTypes->getSingleton(cx, true);
- if (proto) {
- JSObject *templateObject = js_CreateThisForFunctionWithProto(cx, fun, proto);
- if (!templateObject)
- return false;
-
- /*
- * The template incorporates a shape and/or fixed slots from any
- * newScript on its type, so make sure recompilation is triggered
- * should this information change later.
- */
- if (templateObject->type()->newScript)
- types::TypeSet::WatchObjectStateChange(cx, templateObject->type());
-
- RegisterID result = frame.allocReg();
- Jump emptyFreeList = masm.getNewObject(cx, result, templateObject);
-
- stubcc.linkExit(emptyFreeList, Uses(0));
- stubcc.leave();
-
- stubcc.masm.move(ImmPtr(proto), Registers::ArgReg1);
- OOL_STUBCALL(stubs::CreateThis, REJOIN_RESUME);
-
- frame.setThis(result);
-
- stubcc.rejoin(Changes(1));
- return true;
- }
- }
+ if (!proto)
+ break;
+
+ /*
+ * Generate an inline path to create a 'this' object with the given
+ * prototype. Only do this if the type is actually known as a possible
+ * 'this' type of the script.
+ */
+ types::TypeObject *type = proto->getNewType(cx, fun);
+ if (!type)
+ return false;
+ if (!types::TypeScript::ThisTypes(script)->hasType(types::Type::ObjectType(type)))
+ break;
+
+ JSObject *templateObject = js_CreateThisForFunctionWithProto(cx, fun, proto);
+ if (!templateObject)
+ return false;
+
+ /*
+ * The template incorporates a shape and/or fixed slots from any
+ * newScript on its type, so make sure recompilation is triggered
+ * should this information change later.
+ */
+ if (templateObject->type()->newScript)
+ types::TypeSet::WatchObjectStateChange(cx, templateObject->type());
+
+ RegisterID result = frame.allocReg();
+ Jump emptyFreeList = masm.getNewObject(cx, result, templateObject);
+
+ stubcc.linkExit(emptyFreeList, Uses(0));
+ stubcc.leave();
+
+ stubcc.masm.move(ImmPtr(proto), Registers::ArgReg1);
+ OOL_STUBCALL(stubs::CreateThis, REJOIN_RESUME);
+
+ frame.setThis(result);
+
+ stubcc.rejoin(Changes(1));
+ return true;
+ } while (false);
// Load the callee.
frame.pushCallee();
// Get callee.prototype.
if (!jsop_getprop(cx->runtime->atomState.classPrototypeAtom, JSVAL_TYPE_UNKNOWN, false, false))
return false;
@@ -7211,17 +7232,22 @@ mjit::Compiler::updateJoinVarTypes()
/* Update variable types for all new values at this bytecode. */
const SlotValue *newv = analysis->newValues(PC);
if (newv) {
while (newv->slot) {
if (newv->slot < TotalSlots(script)) {
VarType &vt = a->varTypes[newv->slot];
vt.types = analysis->getValueTypes(newv->value);
- vt.type = vt.types->getKnownTypeTag(cx);
+ JSValueType newType = vt.types->getKnownTypeTag(cx);
+ if (newType != vt.type) {
+ FrameEntry *fe = frame.getSlotEntry(newv->slot);
+ frame.forgetLoopReg(fe);
+ }
+ vt.type = newType;
}
newv++;
}
}
}
void
mjit::Compiler::restoreVarType()
--- a/js/src/methodjit/Compiler.h
+++ b/js/src/methodjit/Compiler.h
@@ -467,16 +467,17 @@ class Compiler : public BaseCompiler
Label argsCheckFallthrough;
Jump argsCheckJump;
#endif
bool debugMode_;
bool addTraceHints;
bool inlining_;
bool hasGlobalReallocation;
bool oomInVector; // True if we have OOM'd appending to a vector.
+ bool overflowICSpace; // True if we added a constant pool in a reserved space.
uint32 gcNumber;
enum { NoApplyTricks, LazyArgsObj } applyTricks;
PCLengthEntry *pcLengths;
Compiler *thisFromCtor() { return this; }
friend class CompilerAllocPolicy;
public:
--- a/js/src/methodjit/MachineRegs.h
+++ b/js/src/methodjit/MachineRegs.h
@@ -125,21 +125,23 @@ struct Registers {
#if defined(JS_CPU_X86) || defined(JS_CPU_X64)
static const RegisterID ReturnReg = JSC::X86Registers::eax;
# if defined(JS_CPU_X86) || defined(_WIN64)
static const RegisterID ArgReg0 = JSC::X86Registers::ecx;
static const RegisterID ArgReg1 = JSC::X86Registers::edx;
# if defined(JS_CPU_X64)
static const RegisterID ArgReg2 = JSC::X86Registers::r8;
+ static const RegisterID ArgReg3 = JSC::X86Registers::r9;
# endif
# else
static const RegisterID ArgReg0 = JSC::X86Registers::edi;
static const RegisterID ArgReg1 = JSC::X86Registers::esi;
static const RegisterID ArgReg2 = JSC::X86Registers::edx;
+ static const RegisterID ArgReg3 = JSC::X86Registers::ecx;
# endif
#elif JS_CPU_ARM
static const RegisterID ReturnReg = JSC::ARMRegisters::r0;
static const RegisterID ArgReg0 = JSC::ARMRegisters::r0;
static const RegisterID ArgReg1 = JSC::ARMRegisters::r1;
static const RegisterID ArgReg2 = JSC::ARMRegisters::r2;
#elif JS_CPU_SPARC
static const RegisterID ReturnReg = JSC::SparcRegisters::o0;
@@ -422,17 +424,17 @@ struct Registers {
/* Get a register which is not live before a normal ABI call with at most four args. */
static inline Registers tempCallRegMask() {
Registers regs(AvailRegs);
#ifndef JS_CPU_X86
regs.takeReg(ArgReg0);
regs.takeReg(ArgReg1);
regs.takeReg(ArgReg2);
-#ifdef JS_CPU_SPARC
+#if defined(JS_CPU_SPARC) || defined(JS_CPU_X64)
regs.takeReg(ArgReg3);
#endif
#endif
return regs;
}
Registers(uint32 freeMask)
: freeMask(freeMask)
--- a/js/src/methodjit/Retcon.cpp
+++ b/js/src/methodjit/Retcon.cpp
@@ -80,17 +80,16 @@ AutoScriptRetrapper::untrap(jsbytecode *
static inline JSRejoinState ScriptedRejoin(uint32 pcOffset)
{
return REJOIN_SCRIPTED | (pcOffset << 1);
}
static inline JSRejoinState StubRejoin(RejoinState rejoin)
{
- JS_ASSERT(rejoin != REJOIN_NONE);
return rejoin << 1;
}
static inline void
SetRejoinState(StackFrame *fp, const CallSite &site, void **location)
{
if (site.rejoin == REJOIN_SCRIPTED) {
fp->setRejoin(ScriptedRejoin(site.pcOffset));