Loading js/src/jit-test/tests/cacheir/typeof-proxy.js 0 → 100644 +19 −0 Original line number Diff line number Diff line function test() { var funs = [function() {}, new Proxy(function() {}, {}), wrapWithProto(function() {}, null)]; var objects = [{}, new Proxy({}, {}), wrapWithProto({}, null), new Proxy(objectEmulatingUndefined(), {})]; var undefs = [objectEmulatingUndefined(), wrapWithProto(objectEmulatingUndefined(), null)]; for (var fun of funs) { assertEq(typeof fun, "function") } for (var obj of objects) { assertEq(typeof obj, "object"); } for (var undef of undefs) { assertEq(typeof undef, "undefined"); } } test(); js/src/jit/BaselineIC.cpp +3 −3 Original line number Diff line number Diff line Loading @@ -669,18 +669,18 @@ ICToBool_Object::Compiler::generateStubCode(MacroAssembler& masm) { MOZ_ASSERT(engine_ == Engine::Baseline); Label failure, ifFalse, slowPath; Label failure, emulatesUndefined, slowPath; masm.branchTestObject(Assembler::NotEqual, R0, &failure); Register objReg = masm.extractObject(R0, ExtractTemp0); Register scratch = R1.scratchReg(); masm.branchTestObjectTruthy(false, objReg, scratch, &slowPath, &ifFalse); masm.branchIfObjectEmulatesUndefined(objReg, scratch, &slowPath, &emulatesUndefined); // If object doesn't emulate undefined, it evaulates to true. masm.moveValue(BooleanValue(true), R0); EmitReturnFromIC(masm); masm.bind(&ifFalse); masm.bind(&emulatesUndefined); masm.moveValue(BooleanValue(false), R0); EmitReturnFromIC(masm); Loading js/src/jit/CodeGenerator.cpp +20 −2 Original line number Diff line number Diff line Loading @@ -592,7 +592,7 @@ CodeGenerator::testObjectEmulatesUndefinedKernel(Register objreg, // Perform a fast-path check of the object's class flags if the object's // not a proxy. Let out-of-line code handle the slow cases that require // saving registers, making a function call, and restoring registers. masm.branchTestObjectTruthy(false, objreg, scratch, ool->entry(), ifEmulatesUndefined); masm.branchIfObjectEmulatesUndefined(objreg, scratch, ool->entry(), ifEmulatesUndefined); } void Loading Loading @@ -10657,6 +10657,7 @@ void CodeGenerator::visitOutOfLineTypeOfV(OutOfLineTypeOfV* ool) { LTypeOfV* ins = ool->ins(); const JSAtomState& names = GetJitContext()->runtime->names(); ValueOperand input = ToValue(ins, LTypeOfV::Input); Register temp = ToTempUnboxRegister(ins->tempToUnbox()); Loading @@ -10664,12 +10665,29 @@ CodeGenerator::visitOutOfLineTypeOfV(OutOfLineTypeOfV* ool) Register obj = masm.extractObject(input, temp); Label slowCheck, isObject, isCallable, isUndefined, done; masm.typeOfObject(obj, output, &slowCheck, &isObject, &isCallable, &isUndefined); masm.bind(&isCallable); masm.movePtr(ImmGCPtr(names.function), output); masm.jump(ool->rejoin()); masm.bind(&isUndefined); masm.movePtr(ImmGCPtr(names.undefined), output); masm.jump(ool->rejoin()); masm.bind(&isObject); masm.movePtr(ImmGCPtr(names.object), output); masm.jump(ool->rejoin()); masm.bind(&slowCheck); saveVolatile(output); masm.setupUnalignedABICall(output); masm.passABIArg(obj); masm.movePtr(ImmPtr(GetJitContext()->runtime), output); masm.passABIArg(output); masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, js::TypeOfObjectOperation)); masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, TypeOfObject)); masm.storeCallWordResult(output); restoreVolatile(output); Loading js/src/jit/MacroAssembler-inl.h +14 −16 Original line number Diff line number Diff line Loading @@ -464,6 +464,20 @@ MacroAssembler::branchIfInterpreted(Register fun, Label* label) branchTest32(Assembler::NonZero, address, Imm32(bit), label); } void MacroAssembler::branchIfObjectEmulatesUndefined(Register objReg, Register scratch, Label* slowCheck, Label* label) { // The branches to out-of-line code here implement a conservative version // of the JSObject::isWrapper test performed in EmulatesUndefined. loadObjClass(objReg, scratch); branchTestClassIsProxy(true, scratch, slowCheck); Address flags(scratch, Class::offsetOfFlags()); branchTest32(Assembler::NonZero, flags, Imm32(JSCLASS_EMULATES_UNDEFINED), label); } void MacroAssembler::branchFunctionKind(Condition cond, JSFunction::FunctionKind kind, Register fun, Register scratch, Label* label) Loading Loading @@ -512,22 +526,6 @@ MacroAssembler::branchTestObjGroup(Condition cond, Register obj, Register group, branchPtr(cond, Address(obj, JSObject::offsetOfGroup()), group, label); } void MacroAssembler::branchTestObjectTruthy(bool truthy, Register objReg, Register scratch, Label* slowCheck, Label* checked) { // The branches to out-of-line code here implement a conservative version // of the JSObject::isWrapper test performed in EmulatesUndefined. If none // of the branches are taken, we can check class flags directly. loadObjClass(objReg, scratch); Address flags(scratch, Class::offsetOfFlags()); branchTestClassIsProxy(true, scratch, slowCheck); Condition cond = truthy ? Assembler::Zero : Assembler::NonZero; branchTest32(cond, flags, Imm32(JSCLASS_EMULATES_UNDEFINED), checked); } void MacroAssembler::branchTestClassIsProxy(bool proxy, Register clasp, Label* label) { Loading js/src/jit/MacroAssembler.cpp +27 −0 Original line number Diff line number Diff line Loading @@ -1436,6 +1436,33 @@ MacroAssembler::loadStringIndexValue(Register str, Register dest, Label* fail) rshift32(Imm32(JSString::INDEX_VALUE_SHIFT), dest); } void MacroAssembler::typeOfObject(Register obj, Register scratch, Label* slow, Label* isObject, Label* isCallable, Label* isUndefined) { loadObjClass(obj, scratch); // Proxies can emulate undefined and have complex isCallable behavior. branchTestClassIsProxy(true, scratch, slow); // JSFunctions are always callable. branchPtr(Assembler::Equal, scratch, ImmPtr(&JSFunction::class_), isCallable); // Objects that emulate undefined. Address flags(scratch, Class::offsetOfFlags()); branchTest32(Assembler::NonZero, flags, Imm32(JSCLASS_EMULATES_UNDEFINED), isUndefined); // Handle classes with a call hook. branchPtr(Assembler::Equal, Address(scratch, offsetof(js::Class, cOps)), ImmPtr(nullptr), isObject); loadPtr(Address(scratch, offsetof(js::Class, cOps)), scratch); branchPtr(Assembler::Equal, Address(scratch, offsetof(js::ClassOps, call)), ImmPtr(nullptr), isObject); jump(isCallable); } void MacroAssembler::loadJSContext(Register dest) { Loading Loading
js/src/jit-test/tests/cacheir/typeof-proxy.js 0 → 100644 +19 −0 Original line number Diff line number Diff line function test() { var funs = [function() {}, new Proxy(function() {}, {}), wrapWithProto(function() {}, null)]; var objects = [{}, new Proxy({}, {}), wrapWithProto({}, null), new Proxy(objectEmulatingUndefined(), {})]; var undefs = [objectEmulatingUndefined(), wrapWithProto(objectEmulatingUndefined(), null)]; for (var fun of funs) { assertEq(typeof fun, "function") } for (var obj of objects) { assertEq(typeof obj, "object"); } for (var undef of undefs) { assertEq(typeof undef, "undefined"); } } test();
js/src/jit/BaselineIC.cpp +3 −3 Original line number Diff line number Diff line Loading @@ -669,18 +669,18 @@ ICToBool_Object::Compiler::generateStubCode(MacroAssembler& masm) { MOZ_ASSERT(engine_ == Engine::Baseline); Label failure, ifFalse, slowPath; Label failure, emulatesUndefined, slowPath; masm.branchTestObject(Assembler::NotEqual, R0, &failure); Register objReg = masm.extractObject(R0, ExtractTemp0); Register scratch = R1.scratchReg(); masm.branchTestObjectTruthy(false, objReg, scratch, &slowPath, &ifFalse); masm.branchIfObjectEmulatesUndefined(objReg, scratch, &slowPath, &emulatesUndefined); // If object doesn't emulate undefined, it evaulates to true. masm.moveValue(BooleanValue(true), R0); EmitReturnFromIC(masm); masm.bind(&ifFalse); masm.bind(&emulatesUndefined); masm.moveValue(BooleanValue(false), R0); EmitReturnFromIC(masm); Loading
js/src/jit/CodeGenerator.cpp +20 −2 Original line number Diff line number Diff line Loading @@ -592,7 +592,7 @@ CodeGenerator::testObjectEmulatesUndefinedKernel(Register objreg, // Perform a fast-path check of the object's class flags if the object's // not a proxy. Let out-of-line code handle the slow cases that require // saving registers, making a function call, and restoring registers. masm.branchTestObjectTruthy(false, objreg, scratch, ool->entry(), ifEmulatesUndefined); masm.branchIfObjectEmulatesUndefined(objreg, scratch, ool->entry(), ifEmulatesUndefined); } void Loading Loading @@ -10657,6 +10657,7 @@ void CodeGenerator::visitOutOfLineTypeOfV(OutOfLineTypeOfV* ool) { LTypeOfV* ins = ool->ins(); const JSAtomState& names = GetJitContext()->runtime->names(); ValueOperand input = ToValue(ins, LTypeOfV::Input); Register temp = ToTempUnboxRegister(ins->tempToUnbox()); Loading @@ -10664,12 +10665,29 @@ CodeGenerator::visitOutOfLineTypeOfV(OutOfLineTypeOfV* ool) Register obj = masm.extractObject(input, temp); Label slowCheck, isObject, isCallable, isUndefined, done; masm.typeOfObject(obj, output, &slowCheck, &isObject, &isCallable, &isUndefined); masm.bind(&isCallable); masm.movePtr(ImmGCPtr(names.function), output); masm.jump(ool->rejoin()); masm.bind(&isUndefined); masm.movePtr(ImmGCPtr(names.undefined), output); masm.jump(ool->rejoin()); masm.bind(&isObject); masm.movePtr(ImmGCPtr(names.object), output); masm.jump(ool->rejoin()); masm.bind(&slowCheck); saveVolatile(output); masm.setupUnalignedABICall(output); masm.passABIArg(obj); masm.movePtr(ImmPtr(GetJitContext()->runtime), output); masm.passABIArg(output); masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, js::TypeOfObjectOperation)); masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, TypeOfObject)); masm.storeCallWordResult(output); restoreVolatile(output); Loading
js/src/jit/MacroAssembler-inl.h +14 −16 Original line number Diff line number Diff line Loading @@ -464,6 +464,20 @@ MacroAssembler::branchIfInterpreted(Register fun, Label* label) branchTest32(Assembler::NonZero, address, Imm32(bit), label); } void MacroAssembler::branchIfObjectEmulatesUndefined(Register objReg, Register scratch, Label* slowCheck, Label* label) { // The branches to out-of-line code here implement a conservative version // of the JSObject::isWrapper test performed in EmulatesUndefined. loadObjClass(objReg, scratch); branchTestClassIsProxy(true, scratch, slowCheck); Address flags(scratch, Class::offsetOfFlags()); branchTest32(Assembler::NonZero, flags, Imm32(JSCLASS_EMULATES_UNDEFINED), label); } void MacroAssembler::branchFunctionKind(Condition cond, JSFunction::FunctionKind kind, Register fun, Register scratch, Label* label) Loading Loading @@ -512,22 +526,6 @@ MacroAssembler::branchTestObjGroup(Condition cond, Register obj, Register group, branchPtr(cond, Address(obj, JSObject::offsetOfGroup()), group, label); } void MacroAssembler::branchTestObjectTruthy(bool truthy, Register objReg, Register scratch, Label* slowCheck, Label* checked) { // The branches to out-of-line code here implement a conservative version // of the JSObject::isWrapper test performed in EmulatesUndefined. If none // of the branches are taken, we can check class flags directly. loadObjClass(objReg, scratch); Address flags(scratch, Class::offsetOfFlags()); branchTestClassIsProxy(true, scratch, slowCheck); Condition cond = truthy ? Assembler::Zero : Assembler::NonZero; branchTest32(cond, flags, Imm32(JSCLASS_EMULATES_UNDEFINED), checked); } void MacroAssembler::branchTestClassIsProxy(bool proxy, Register clasp, Label* label) { Loading
js/src/jit/MacroAssembler.cpp +27 −0 Original line number Diff line number Diff line Loading @@ -1436,6 +1436,33 @@ MacroAssembler::loadStringIndexValue(Register str, Register dest, Label* fail) rshift32(Imm32(JSString::INDEX_VALUE_SHIFT), dest); } void MacroAssembler::typeOfObject(Register obj, Register scratch, Label* slow, Label* isObject, Label* isCallable, Label* isUndefined) { loadObjClass(obj, scratch); // Proxies can emulate undefined and have complex isCallable behavior. branchTestClassIsProxy(true, scratch, slow); // JSFunctions are always callable. branchPtr(Assembler::Equal, scratch, ImmPtr(&JSFunction::class_), isCallable); // Objects that emulate undefined. Address flags(scratch, Class::offsetOfFlags()); branchTest32(Assembler::NonZero, flags, Imm32(JSCLASS_EMULATES_UNDEFINED), isUndefined); // Handle classes with a call hook. branchPtr(Assembler::Equal, Address(scratch, offsetof(js::Class, cOps)), ImmPtr(nullptr), isObject); loadPtr(Address(scratch, offsetof(js::Class, cOps)), scratch); branchPtr(Assembler::Equal, Address(scratch, offsetof(js::ClassOps, call)), ImmPtr(nullptr), isObject); jump(isCallable); } void MacroAssembler::loadJSContext(Register dest) { Loading