summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--js/src/jit/BaselineCompiler.cpp20
-rw-r--r--js/src/jit/BaselineCompiler.h1
-rw-r--r--js/src/jit/CodeGenerator.cpp109
-rw-r--r--js/src/jit/CodeGenerator.h7
-rw-r--r--js/src/jit/IonBuilder.cpp12
-rw-r--r--js/src/jit/IonBuilder.h1
-rw-r--r--js/src/jit/Lowering.cpp13
-rw-r--r--js/src/jit/Lowering.h1
-rw-r--r--js/src/jit/MIR.h32
-rw-r--r--js/src/jit/MOpcodes.h1
-rw-r--r--js/src/jit/VMFunctions.cpp9
-rw-r--r--js/src/jit/VMFunctions.h4
-rw-r--r--js/src/jit/shared/LIR-shared.h21
-rw-r--r--js/src/jit/shared/LOpcodes-shared.h1
14 files changed, 189 insertions, 43 deletions
diff --git a/js/src/jit/BaselineCompiler.cpp b/js/src/jit/BaselineCompiler.cpp
index 4524bae07..07d8e629d 100644
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -1381,6 +1381,26 @@ BaselineCompiler::emit_JSOP_CHECKISOBJ()
return true;
}
+typedef bool (*CheckIsCallableFn)(JSContext*, HandleValue, CheckIsCallableKind);
+static const VMFunction CheckIsCallableInfo =
+ FunctionInfo<CheckIsCallableFn>(CheckIsCallable, "CheckIsCallable");
+
+bool
+BaselineCompiler::emit_JSOP_CHECKISCALLABLE()
+{
+ frame.syncStack(0);
+ masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0);
+
+ prepareVMCall();
+
+ pushArg(Imm32(GET_UINT8(pc)));
+ pushArg(R0);
+ if (!callVM(CheckIsCallableInfo))
+ return false;
+
+ return true;
+}
+
typedef bool (*ThrowUninitializedThisFn)(JSContext*, BaselineFrame* frame);
static const VMFunction ThrowUninitializedThisInfo =
FunctionInfo<ThrowUninitializedThisFn>(BaselineThrowUninitializedThis,
diff --git a/js/src/jit/BaselineCompiler.h b/js/src/jit/BaselineCompiler.h
index 18e56bcd4..0bacf6f18 100644
--- a/js/src/jit/BaselineCompiler.h
+++ b/js/src/jit/BaselineCompiler.h
@@ -218,6 +218,7 @@ namespace jit {
_(JSOP_FUNCTIONTHIS) \
_(JSOP_GLOBALTHIS) \
_(JSOP_CHECKISOBJ) \
+ _(JSOP_CHECKISCALLABLE) \
_(JSOP_CHECKTHIS) \
_(JSOP_CHECKRETURN) \
_(JSOP_NEWTARGET) \
diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp
index 3b5ec6baa..ccdc5fbfa 100644
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -11326,25 +11326,35 @@ class OutOfLineIsCallable : public OutOfLineCodeBase<CodeGenerator>
}
};
+template <CodeGenerator::CallableOrConstructor mode>
void
-CodeGenerator::visitIsCallable(LIsCallable* ins)
+CodeGenerator::emitIsCallableOrConstructor(Register object, Register output, Label* failure)
{
- Register object = ToRegister(ins->object());
- Register output = ToRegister(ins->output());
-
- OutOfLineIsCallable* ool = new(alloc()) OutOfLineIsCallable(ins);
- addOutOfLineCode(ool, ins->mir());
-
Label notFunction, hasCOps, done;
masm.loadObjClass(object, output);
- // Just skim proxies off. Their notion of isCallable() is more complicated.
- masm.branchTestClassIsProxy(true, output, ool->entry());
+ // Just skim proxies off. Their notion of isCallable()/isConstructor() is
+ // more complicated.
+ masm.branchTestClassIsProxy(true, output, failure);
// An object is callable iff:
// is<JSFunction>() || (getClass()->cOps && getClass()->cOps->call).
+ // An object is constructor iff:
+ // ((is<JSFunction>() && as<JSFunction>().isConstructor) ||
+ // (getClass()->cOps && getClass()->cOps->construct)).
masm.branchPtr(Assembler::NotEqual, output, ImmPtr(&JSFunction::class_), &notFunction);
- masm.move32(Imm32(1), output);
+ if (mode == Callable) {
+ masm.move32(Imm32(1), output);
+ } else {
+ Label notConstructor;
+ masm.load16ZeroExtend(Address(object, JSFunction::offsetOfFlags()), output);
+ masm.and32(Imm32(JSFunction::CONSTRUCTOR), output);
+ masm.branchTest32(Assembler::Zero, output, output, &notConstructor);
+ masm.move32(Imm32(1), output);
+ masm.jump(&done);
+ masm.bind(&notConstructor);
+ masm.move32(Imm32(0), output);
+ }
masm.jump(&done);
masm.bind(&notFunction);
@@ -11355,10 +11365,26 @@ CodeGenerator::visitIsCallable(LIsCallable* ins)
masm.bind(&hasCOps);
masm.loadPtr(Address(output, offsetof(js::Class, cOps)), output);
- masm.cmpPtrSet(Assembler::NonZero, Address(output, offsetof(js::ClassOps, call)),
+ size_t opsOffset = mode == Callable
+ ? offsetof(js::ClassOps, call)
+ : offsetof(js::ClassOps, construct);
+ masm.cmpPtrSet(Assembler::NonZero, Address(output, opsOffset),
ImmPtr(nullptr), output);
masm.bind(&done);
+}
+
+void
+CodeGenerator::visitIsCallable(LIsCallable* ins)
+{
+ Register object = ToRegister(ins->object());
+ Register output = ToRegister(ins->output());
+
+ OutOfLineIsCallable* ool = new(alloc()) OutOfLineIsCallable(ins);
+ addOutOfLineCode(ool, ins->mir());
+
+ emitIsCallableOrConstructor<Callable>(object, output, ool->entry());
+
masm.bind(ool->rejoin());
}
@@ -11378,6 +11404,36 @@ CodeGenerator::visitOutOfLineIsCallable(OutOfLineIsCallable* ool)
masm.jump(ool->rejoin());
}
+typedef bool (*CheckIsCallableFn)(JSContext*, HandleValue, CheckIsCallableKind);
+static const VMFunction CheckIsCallableInfo =
+ FunctionInfo<CheckIsCallableFn>(CheckIsCallable, "CheckIsCallable");
+
+void
+CodeGenerator::visitCheckIsCallable(LCheckIsCallable* ins)
+{
+ ValueOperand checkValue = ToValue(ins, LCheckIsCallable::CheckValue);
+ Register temp = ToRegister(ins->temp());
+
+ // OOL code is used in the following 2 cases:
+ // * checkValue is not callable
+ // * checkValue is proxy and it's unknown whether it's callable or not
+ // CheckIsCallable checks if passed value is callable, regardless of the
+ // cases above. IsCallable operation is not observable and checking it
+ // again doesn't matter.
+ OutOfLineCode* ool = oolCallVM(CheckIsCallableInfo, ins,
+ ArgList(checkValue, Imm32(ins->mir()->checkKind())),
+ StoreNothing());
+
+ masm.branchTestObject(Assembler::NotEqual, checkValue, ool->entry());
+
+ Register object = masm.extractObject(checkValue, temp);
+ emitIsCallableOrConstructor<Callable>(object, temp, ool->entry());
+
+ masm.branchTest32(Assembler::Zero, temp, temp, ool->entry());
+
+ masm.bind(ool->rejoin());
+}
+
class OutOfLineIsConstructor : public OutOfLineCodeBase<CodeGenerator>
{
LIsConstructor* ins_;
@@ -11404,37 +11460,8 @@ CodeGenerator::visitIsConstructor(LIsConstructor* ins)
OutOfLineIsConstructor* ool = new(alloc()) OutOfLineIsConstructor(ins);
addOutOfLineCode(ool, ins->mir());
- Label notFunction, notConstructor, hasCOps, done;
- masm.loadObjClass(object, output);
-
- // Just skim proxies off. Their notion of isConstructor() is more complicated.
- masm.branchTestClassIsProxy(true, output, ool->entry());
+ emitIsCallableOrConstructor<Constructor>(object, output, ool->entry());
- // An object is constructor iff
- // ((is<JSFunction>() && as<JSFunction>().isConstructor) ||
- // (getClass()->cOps && getClass()->cOps->construct)).
- masm.branchPtr(Assembler::NotEqual, output, ImmPtr(&JSFunction::class_), &notFunction);
- masm.load16ZeroExtend(Address(object, JSFunction::offsetOfFlags()), output);
- masm.and32(Imm32(JSFunction::CONSTRUCTOR), output);
- masm.branchTest32(Assembler::Zero, output, output, &notConstructor);
- masm.move32(Imm32(1), output);
- masm.jump(&done);
- masm.bind(&notConstructor);
- masm.move32(Imm32(0), output);
- masm.jump(&done);
-
- masm.bind(&notFunction);
- masm.branchPtr(Assembler::NonZero, Address(output, offsetof(js::Class, cOps)),
- ImmPtr(nullptr), &hasCOps);
- masm.move32(Imm32(0), output);
- masm.jump(&done);
-
- masm.bind(&hasCOps);
- masm.loadPtr(Address(output, offsetof(js::Class, cOps)), output);
- masm.cmpPtrSet(Assembler::NonZero, Address(output, offsetof(js::ClassOps, construct)),
- ImmPtr(nullptr), output);
-
- masm.bind(&done);
masm.bind(ool->rejoin());
}
diff --git a/js/src/jit/CodeGenerator.h b/js/src/jit/CodeGenerator.h
index b69e919a3..d3126651b 100644
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -364,6 +364,12 @@ class CodeGenerator final : public CodeGeneratorSpecific
void visitCallDOMNative(LCallDOMNative* lir);
void visitCallGetIntrinsicValue(LCallGetIntrinsicValue* lir);
void visitCallBindVar(LCallBindVar* lir);
+ enum CallableOrConstructor {
+ Callable,
+ Constructor
+ };
+ template <CallableOrConstructor mode>
+ void emitIsCallableOrConstructor(Register object, Register output, Label* failure);
void visitIsCallable(LIsCallable* lir);
void visitOutOfLineIsCallable(OutOfLineIsCallable* ool);
void visitIsConstructor(LIsConstructor* lir);
@@ -384,6 +390,7 @@ class CodeGenerator final : public CodeGeneratorSpecific
void visitArrowNewTarget(LArrowNewTarget* ins);
void visitCheckReturn(LCheckReturn* ins);
void visitCheckIsObj(LCheckIsObj* ins);
+ void visitCheckIsCallable(LCheckIsCallable* ins);
void visitCheckObjCoercible(LCheckObjCoercible* ins);
void visitDebugCheckSelfHosted(LDebugCheckSelfHosted* ins);
void visitNaNToZero(LNaNToZero* ins);
diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp
index 26bba0656..ed09fb504 100644
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -2183,6 +2183,9 @@ IonBuilder::inspectOpcode(JSOp op)
case JSOP_CHECKISOBJ:
return jsop_checkisobj(GET_UINT8(pc));
+ case JSOP_CHECKISCALLABLE:
+ return jsop_checkiscallable(GET_UINT8(pc));
+
case JSOP_CHECKOBJCOERCIBLE:
return jsop_checkobjcoercible();
@@ -10900,6 +10903,15 @@ IonBuilder::jsop_checkisobj(uint8_t kind)
}
bool
+IonBuilder::jsop_checkiscallable(uint8_t kind)
+{
+ MCheckIsCallable* check = MCheckIsCallable::New(alloc(), current->pop(), kind);
+ current->add(check);
+ current->push(check);
+ return true;
+}
+
+bool
IonBuilder::jsop_checkobjcoercible()
{
MDefinition* toCheck = current->peek(-1);
diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h
index c3cd9700a..35ad120f7 100644
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -783,6 +783,7 @@ class IonBuilder
MOZ_MUST_USE bool jsop_debugger();
MOZ_MUST_USE bool jsop_newtarget();
MOZ_MUST_USE bool jsop_checkisobj(uint8_t kind);
+ MOZ_MUST_USE bool jsop_checkiscallable(uint8_t kind);
MOZ_MUST_USE bool jsop_checkobjcoercible();
MOZ_MUST_USE bool jsop_pushcallobj();
diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp
index a21a529be..7f28a9020 100644
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -4689,6 +4689,19 @@ LIRGenerator::visitCheckIsObj(MCheckIsObj* ins)
}
void
+LIRGenerator::visitCheckIsCallable(MCheckIsCallable* ins)
+{
+ MDefinition* checkVal = ins->checkValue();
+ MOZ_ASSERT(checkVal->type() == MIRType::Value);
+
+ LCheckIsCallable* lir = new(alloc()) LCheckIsCallable(useBox(checkVal),
+ temp());
+ redefine(ins, checkVal);
+ add(lir, ins);
+ assignSafepoint(lir, ins);
+}
+
+void
LIRGenerator::visitCheckObjCoercible(MCheckObjCoercible* ins)
{
MDefinition* checkVal = ins->checkValue();
diff --git a/js/src/jit/Lowering.h b/js/src/jit/Lowering.h
index 4062c0960..b2805cb7a 100644
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -329,6 +329,7 @@ class LIRGenerator : public LIRGeneratorSpecific
void visitGuardSharedTypedArray(MGuardSharedTypedArray* ins);
void visitCheckReturn(MCheckReturn* ins);
void visitCheckIsObj(MCheckIsObj* ins);
+ void visitCheckIsCallable(MCheckIsCallable* ins);
void visitCheckObjCoercible(MCheckObjCoercible* ins);
void visitDebugCheckSelfHosted(MDebugCheckSelfHosted* ins);
};
diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h
index 3caa7e357..2de91e2df 100644
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -13455,8 +13455,9 @@ class MCheckIsObj
{
uint8_t checkKind_;
- explicit MCheckIsObj(MDefinition* toCheck, uint8_t checkKind)
- : MUnaryInstruction(toCheck), checkKind_(checkKind)
+ MCheckIsObj(MDefinition* toCheck, uint8_t checkKind)
+ : MUnaryInstruction(toCheck),
+ checkKind_(checkKind)
{
setResultType(MIRType::Value);
setResultTypeSet(toCheck->resultTypeSet());
@@ -13475,6 +13476,33 @@ class MCheckIsObj
}
};
+class MCheckIsCallable
+ : public MUnaryInstruction,
+ public BoxInputsPolicy::Data
+{
+ uint8_t checkKind_;
+
+ MCheckIsCallable(MDefinition* toCheck, uint8_t checkKind)
+ : MUnaryInstruction(toCheck),
+ checkKind_(checkKind)
+ {
+ setResultType(MIRType::Value);
+ setResultTypeSet(toCheck->resultTypeSet());
+ setGuard();
+ }
+
+ public:
+ INSTRUCTION_HEADER(CheckIsCallable)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, checkValue))
+
+ uint8_t checkKind() const { return checkKind_; }
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+};
+
class MCheckObjCoercible
: public MUnaryInstruction,
public BoxInputsPolicy::Data
diff --git a/js/src/jit/MOpcodes.h b/js/src/jit/MOpcodes.h
index 1a6911247..bb2ab8190 100644
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -285,6 +285,7 @@ namespace jit {
_(ArrowNewTarget) \
_(CheckReturn) \
_(CheckIsObj) \
+ _(CheckIsCallable) \
_(CheckObjCoercible) \
_(DebugCheckSelfHosted) \
_(AsmJSNeg) \
diff --git a/js/src/jit/VMFunctions.cpp b/js/src/jit/VMFunctions.cpp
index 4edbc3c83..77b9e3647 100644
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -1349,5 +1349,14 @@ BaselineGetFunctionThis(JSContext* cx, BaselineFrame* frame, MutableHandleValue
return GetFunctionThis(cx, frame, res);
}
+bool
+CheckIsCallable(JSContext* cx, HandleValue v, CheckIsCallableKind kind)
+{
+ if (!IsCallable(v))
+ return ThrowCheckIsCallable(cx, kind);
+
+ return true;
+}
+
} // namespace jit
} // namespace js
diff --git a/js/src/jit/VMFunctions.h b/js/src/jit/VMFunctions.h
index f754d58c7..572f05373 100644
--- a/js/src/jit/VMFunctions.h
+++ b/js/src/jit/VMFunctions.h
@@ -13,6 +13,7 @@
#include "jit/CompileInfo.h"
#include "jit/JitFrames.h"
+#include "vm/Interpreter.h"
namespace js {
@@ -802,6 +803,9 @@ ThrowObjectCoercible(JSContext* cx, HandleValue v);
MOZ_MUST_USE bool
BaselineGetFunctionThis(JSContext* cx, BaselineFrame* frame, MutableHandleValue res);
+MOZ_MUST_USE bool
+CheckIsCallable(JSContext* cx, HandleValue v, CheckIsCallableKind kind);
+
} // namespace jit
} // namespace js
diff --git a/js/src/jit/shared/LIR-shared.h b/js/src/jit/shared/LIR-shared.h
index f8e0ce9cc..9dcb527c5 100644
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -8893,6 +8893,27 @@ class LCheckIsObj : public LInstructionHelper<BOX_PIECES, BOX_PIECES, 0>
}
};
+class LCheckIsCallable : public LInstructionHelper<BOX_PIECES, BOX_PIECES, 1>
+{
+ public:
+ LIR_HEADER(CheckIsCallable)
+
+ static const size_t CheckValue = 0;
+
+ LCheckIsCallable(const LBoxAllocation& value, const LDefinition& temp) {
+ setBoxOperand(CheckValue, value);
+ setTemp(0, temp);
+ }
+
+ const LDefinition* temp() {
+ return getTemp(0);
+ }
+
+ MCheckIsCallable* mir() const {
+ return mir_->toCheckIsCallable();
+ }
+};
+
class LCheckObjCoercible : public LCallInstructionHelper<BOX_PIECES, BOX_PIECES, 0>
{
public:
diff --git a/js/src/jit/shared/LOpcodes-shared.h b/js/src/jit/shared/LOpcodes-shared.h
index e57751437..3eea1b449 100644
--- a/js/src/jit/shared/LOpcodes-shared.h
+++ b/js/src/jit/shared/LOpcodes-shared.h
@@ -402,6 +402,7 @@
_(ArrowNewTarget) \
_(CheckReturn) \
_(CheckIsObj) \
+ _(CheckIsCallable) \
_(CheckObjCoercible) \
_(DebugCheckSelfHosted) \
_(AsmJSLoadHeap) \