[es6] Reintroduce the instanceof operator in the backends.

This adds back the instanceof operator support in the backends and
introduces a @@hasInstance protector cell on the isolate that guards the
fast path for the InstanceOfStub. This way we recover the ~10%
regression on Octane EarleyBoyer in Crankshaft and greatly improve
TurboFan and Ignition performance of instanceof.

R=ishell@chromium.org
TBR=hpayer@chromium.org,rossberg@chromium.org
BUG=chromium:597249, v8:4447
LOG=n

Review-Url: https://codereview.chromium.org/1980483003
Cr-Commit-Position: refs/heads/master@{#36275}
This commit is contained in:
bmeurer 2016-05-17 04:23:59 -07:00 committed by Commit bot
parent 565730666e
commit 551e0aa11b
94 changed files with 508 additions and 1739 deletions

View File

@ -1747,28 +1747,6 @@ void Builtins::Generate_DatePrototype_GetField(MacroAssembler* masm,
__ TailCallRuntime(Runtime::kThrowNotDateError);
}
// static
void Builtins::Generate_FunctionHasInstance(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- r0 : argc
// -- sp[0] : first argument (left-hand side)
// -- sp[4] : receiver (right-hand side)
// -----------------------------------
{
FrameScope scope(masm, StackFrame::INTERNAL);
__ ldr(InstanceOfDescriptor::LeftRegister(),
MemOperand(fp, 2 * kPointerSize)); // Load left-hand side.
__ ldr(InstanceOfDescriptor::RightRegister(),
MemOperand(fp, 3 * kPointerSize)); // Load right-hand side.
InstanceOfStub stub(masm->isolate(), true);
__ CallStub(&stub);
}
// Pop the argument and the receiver.
__ Ret(2);
}
// static
void Builtins::Generate_FunctionPrototypeApply(MacroAssembler* masm) {
// ----------- S t a t e -------------

View File

@ -1319,126 +1319,6 @@ void JSEntryStub::Generate(MacroAssembler* masm) {
}
void InstanceOfStub::Generate(MacroAssembler* masm) {
Register const object = r1; // Object (lhs).
Register const function = r0; // Function (rhs).
Register const object_map = r2; // Map of {object}.
Register const function_map = r3; // Map of {function}.
Register const function_prototype = r4; // Prototype of {function}.
Register const scratch = r5;
DCHECK(object.is(InstanceOfDescriptor::LeftRegister()));
DCHECK(function.is(InstanceOfDescriptor::RightRegister()));
// Check if {object} is a smi.
Label object_is_smi;
__ JumpIfSmi(object, &object_is_smi);
// Lookup the {function} and the {object} map in the global instanceof cache.
// Note: This is safe because we clear the global instanceof cache whenever
// we change the prototype of any object.
Label fast_case, slow_case;
__ ldr(object_map, FieldMemOperand(object, HeapObject::kMapOffset));
__ CompareRoot(function, Heap::kInstanceofCacheFunctionRootIndex);
__ b(ne, &fast_case);
__ CompareRoot(object_map, Heap::kInstanceofCacheMapRootIndex);
__ b(ne, &fast_case);
__ LoadRoot(r0, Heap::kInstanceofCacheAnswerRootIndex);
__ Ret();
// If {object} is a smi we can safely return false if {function} is a JS
// function, otherwise we have to miss to the runtime and throw an exception.
__ bind(&object_is_smi);
__ JumpIfSmi(function, &slow_case);
__ CompareObjectType(function, function_map, scratch, JS_FUNCTION_TYPE);
__ b(ne, &slow_case);
__ LoadRoot(r0, Heap::kFalseValueRootIndex);
__ Ret();
// Fast-case: The {function} must be a valid JSFunction.
__ bind(&fast_case);
__ JumpIfSmi(function, &slow_case);
__ CompareObjectType(function, function_map, scratch, JS_FUNCTION_TYPE);
__ b(ne, &slow_case);
// Go to the runtime if the function is not a constructor.
__ ldrb(scratch, FieldMemOperand(function_map, Map::kBitFieldOffset));
__ tst(scratch, Operand(1 << Map::kIsConstructor));
__ b(eq, &slow_case);
// Ensure that {function} has an instance prototype.
__ tst(scratch, Operand(1 << Map::kHasNonInstancePrototype));
__ b(ne, &slow_case);
// Get the "prototype" (or initial map) of the {function}.
__ ldr(function_prototype,
FieldMemOperand(function, JSFunction::kPrototypeOrInitialMapOffset));
__ AssertNotSmi(function_prototype);
// Resolve the prototype if the {function} has an initial map. Afterwards the
// {function_prototype} will be either the JSReceiver prototype object or the
// hole value, which means that no instances of the {function} were created so
// far and hence we should return false.
Label function_prototype_valid;
__ CompareObjectType(function_prototype, scratch, scratch, MAP_TYPE);
__ b(ne, &function_prototype_valid);
__ ldr(function_prototype,
FieldMemOperand(function_prototype, Map::kPrototypeOffset));
__ bind(&function_prototype_valid);
__ AssertNotSmi(function_prototype);
// Update the global instanceof cache with the current {object} map and
// {function}. The cached answer will be set when it is known below.
__ StoreRoot(function, Heap::kInstanceofCacheFunctionRootIndex);
__ StoreRoot(object_map, Heap::kInstanceofCacheMapRootIndex);
// Loop through the prototype chain looking for the {function} prototype.
// Assume true, and change to false if not found.
Register const object_instance_type = function_map;
Register const map_bit_field = function_map;
Register const null = scratch;
Register const result = r0;
Label done, loop, fast_runtime_fallback;
__ LoadRoot(result, Heap::kTrueValueRootIndex);
__ LoadRoot(null, Heap::kNullValueRootIndex);
__ bind(&loop);
// Check if the object needs to be access checked.
__ ldrb(map_bit_field, FieldMemOperand(object_map, Map::kBitFieldOffset));
__ tst(map_bit_field, Operand(1 << Map::kIsAccessCheckNeeded));
__ b(ne, &fast_runtime_fallback);
// Check if the current object is a Proxy.
__ CompareInstanceType(object_map, object_instance_type, JS_PROXY_TYPE);
__ b(eq, &fast_runtime_fallback);
__ ldr(object, FieldMemOperand(object_map, Map::kPrototypeOffset));
__ cmp(object, function_prototype);
__ b(eq, &done);
__ cmp(object, null);
__ ldr(object_map, FieldMemOperand(object, HeapObject::kMapOffset));
__ b(ne, &loop);
__ LoadRoot(result, Heap::kFalseValueRootIndex);
__ bind(&done);
__ StoreRoot(result, Heap::kInstanceofCacheAnswerRootIndex);
__ Ret();
// Found Proxy or access check needed: Call the runtime
__ bind(&fast_runtime_fallback);
__ Push(object, function_prototype);
// Invalidate the instanceof cache.
__ Move(scratch, Smi::FromInt(0));
__ StoreRoot(scratch, Heap::kInstanceofCacheFunctionRootIndex);
__ TailCallRuntime(Runtime::kHasInPrototypeChain);
// Slow-case: Call the %InstanceOf runtime function.
__ bind(&slow_case);
__ Push(object, function);
__ TailCallRuntime(is_es6_instanceof() ? Runtime::kOrdinaryHasInstance
: Runtime::kInstanceOf);
}
void FunctionPrototypeStub::Generate(MacroAssembler* masm) {
Label miss;
Register receiver = LoadDescriptor::ReceiverRegister();

View File

@ -48,10 +48,6 @@ const Register StoreGlobalViaContextDescriptor::SlotRegister() { return r2; }
const Register StoreGlobalViaContextDescriptor::ValueRegister() { return r0; }
const Register InstanceOfDescriptor::LeftRegister() { return r1; }
const Register InstanceOfDescriptor::RightRegister() { return r0; }
const Register StringCompareDescriptor::LeftRegister() { return r1; }
const Register StringCompareDescriptor::RightRegister() { return r0; }

View File

@ -1682,30 +1682,6 @@ void Builtins::Generate_DatePrototype_GetField(MacroAssembler* masm,
__ TailCallRuntime(Runtime::kThrowNotDateError);
}
// static
void Builtins::Generate_FunctionHasInstance(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- x0 : argc
// -- jssp[0] : first argument (left-hand side)
// -- jssp[8] : receiver (right-hand side)
// -----------------------------------
ASM_LOCATION("Builtins::Generate_FunctionHasInstance");
{
FrameScope scope(masm, StackFrame::INTERNAL);
__ Ldr(InstanceOfDescriptor::LeftRegister(),
MemOperand(fp, 2 * kPointerSize)); // Load left-hand side.
__ Ldr(InstanceOfDescriptor::RightRegister(),
MemOperand(fp, 3 * kPointerSize)); // Load right-hand side.
InstanceOfStub stub(masm->isolate(), true);
__ CallStub(&stub);
}
// Pop the argument and the receiver.
__ Drop(2);
__ Ret();
}
// static
void Builtins::Generate_FunctionPrototypeApply(MacroAssembler* masm) {
// ----------- S t a t e -------------

View File

@ -1504,123 +1504,6 @@ void LoadIndexedStringStub::Generate(MacroAssembler* masm) {
}
void InstanceOfStub::Generate(MacroAssembler* masm) {
Register const object = x1; // Object (lhs).
Register const function = x0; // Function (rhs).
Register const object_map = x2; // Map of {object}.
Register const function_map = x3; // Map of {function}.
Register const function_prototype = x4; // Prototype of {function}.
Register const scratch = x5;
DCHECK(object.is(InstanceOfDescriptor::LeftRegister()));
DCHECK(function.is(InstanceOfDescriptor::RightRegister()));
// Check if {object} is a smi.
Label object_is_smi;
__ JumpIfSmi(object, &object_is_smi);
// Lookup the {function} and the {object} map in the global instanceof cache.
// Note: This is safe because we clear the global instanceof cache whenever
// we change the prototype of any object.
Label fast_case, slow_case;
__ Ldr(object_map, FieldMemOperand(object, HeapObject::kMapOffset));
__ JumpIfNotRoot(function, Heap::kInstanceofCacheFunctionRootIndex,
&fast_case);
__ JumpIfNotRoot(object_map, Heap::kInstanceofCacheMapRootIndex, &fast_case);
__ LoadRoot(x0, Heap::kInstanceofCacheAnswerRootIndex);
__ Ret();
// If {object} is a smi we can safely return false if {function} is a JS
// function, otherwise we have to miss to the runtime and throw an exception.
__ Bind(&object_is_smi);
__ JumpIfSmi(function, &slow_case);
__ JumpIfNotObjectType(function, function_map, scratch, JS_FUNCTION_TYPE,
&slow_case);
__ LoadRoot(x0, Heap::kFalseValueRootIndex);
__ Ret();
// Fast-case: The {function} must be a valid JSFunction.
__ Bind(&fast_case);
__ JumpIfSmi(function, &slow_case);
__ JumpIfNotObjectType(function, function_map, scratch, JS_FUNCTION_TYPE,
&slow_case);
// Go to the runtime if the function is not a constructor.
__ Ldrb(scratch, FieldMemOperand(function_map, Map::kBitFieldOffset));
__ Tbz(scratch, Map::kIsConstructor, &slow_case);
// Ensure that {function} has an instance prototype.
__ Tbnz(scratch, Map::kHasNonInstancePrototype, &slow_case);
// Get the "prototype" (or initial map) of the {function}.
__ Ldr(function_prototype,
FieldMemOperand(function, JSFunction::kPrototypeOrInitialMapOffset));
__ AssertNotSmi(function_prototype);
// Resolve the prototype if the {function} has an initial map. Afterwards the
// {function_prototype} will be either the JSReceiver prototype object or the
// hole value, which means that no instances of the {function} were created so
// far and hence we should return false.
Label function_prototype_valid;
__ JumpIfNotObjectType(function_prototype, scratch, scratch, MAP_TYPE,
&function_prototype_valid);
__ Ldr(function_prototype,
FieldMemOperand(function_prototype, Map::kPrototypeOffset));
__ Bind(&function_prototype_valid);
__ AssertNotSmi(function_prototype);
// Update the global instanceof cache with the current {object} map and
// {function}. The cached answer will be set when it is known below.
__ StoreRoot(function, Heap::kInstanceofCacheFunctionRootIndex);
__ StoreRoot(object_map, Heap::kInstanceofCacheMapRootIndex);
// Loop through the prototype chain looking for the {function} prototype.
// Assume true, and change to false if not found.
Register const object_instance_type = function_map;
Register const map_bit_field = function_map;
Register const null = scratch;
Register const result = x0;
Label done, loop, fast_runtime_fallback;
__ LoadRoot(result, Heap::kTrueValueRootIndex);
__ LoadRoot(null, Heap::kNullValueRootIndex);
__ Bind(&loop);
// Check if the object needs to be access checked.
__ Ldrb(map_bit_field, FieldMemOperand(object_map, Map::kBitFieldOffset));
__ TestAndBranchIfAnySet(map_bit_field, 1 << Map::kIsAccessCheckNeeded,
&fast_runtime_fallback);
// Check if the current object is a Proxy.
__ CompareInstanceType(object_map, object_instance_type, JS_PROXY_TYPE);
__ B(eq, &fast_runtime_fallback);
__ Ldr(object, FieldMemOperand(object_map, Map::kPrototypeOffset));
__ Cmp(object, function_prototype);
__ B(eq, &done);
__ Cmp(object, null);
__ Ldr(object_map, FieldMemOperand(object, HeapObject::kMapOffset));
__ B(ne, &loop);
__ LoadRoot(result, Heap::kFalseValueRootIndex);
__ Bind(&done);
__ StoreRoot(result, Heap::kInstanceofCacheAnswerRootIndex);
__ Ret();
// Found Proxy or access check needed: Call the runtime
__ Bind(&fast_runtime_fallback);
__ Push(object, function_prototype);
// Invalidate the instanceof cache.
__ Move(scratch, Smi::FromInt(0));
__ StoreRoot(scratch, Heap::kInstanceofCacheFunctionRootIndex);
__ TailCallRuntime(Runtime::kHasInPrototypeChain);
// Slow-case: Call the %InstanceOf runtime function.
__ bind(&slow_case);
__ Push(object, function);
__ TailCallRuntime(is_es6_instanceof() ? Runtime::kOrdinaryHasInstance
: Runtime::kInstanceOf);
}
void RegExpExecStub::Generate(MacroAssembler* masm) {
#ifdef V8_INTERPRETED_REGEXP
__ TailCallRuntime(Runtime::kRegExpExec);

View File

@ -48,10 +48,6 @@ const Register StoreGlobalViaContextDescriptor::SlotRegister() { return x2; }
const Register StoreGlobalViaContextDescriptor::ValueRegister() { return x0; }
const Register InstanceOfDescriptor::LeftRegister() { return x1; }
const Register InstanceOfDescriptor::RightRegister() { return x0; }
const Register StringCompareDescriptor::LeftRegister() { return x1; }
const Register StringCompareDescriptor::RightRegister() { return x0; }

View File

@ -1189,7 +1189,7 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
Handle<JSFunction> has_instance = InstallFunction(
prototype, factory->has_instance_symbol(), JS_OBJECT_TYPE,
JSObject::kHeaderSize, MaybeHandle<JSObject>(),
Builtins::kFunctionHasInstance,
Builtins::kFunctionPrototypeHasInstance,
static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE | READ_ONLY));
// Set the expected parameters for @@hasInstance to 1; required by builtin.
@ -1198,9 +1198,6 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
// Set the length for the function to satisfy ECMA-262.
has_instance->shared()->set_length(1);
// Install in the native context
native_context()->set_ordinary_has_instance(*has_instance);
// Install the "constructor" property on the %FunctionPrototype%.
JSObject::AddProperty(prototype, factory->constructor_string(),
function_fun, DONT_ENUM);

View File

@ -2414,6 +2414,21 @@ void Builtins::Generate_MathTrunc(CodeStubAssembler* assembler) {
Generate_MathRoundingOperation(assembler, &CodeStubAssembler::Float64Trunc);
}
// -----------------------------------------------------------------------------
// ES6 section 19.2 Function Objects
// ES6 section 19.2.3.6 Function.prototype [ @@hasInstance ] ( V )
void Builtins::Generate_FunctionPrototypeHasInstance(
CodeStubAssembler* assembler) {
using compiler::Node;
Node* f = assembler->Parameter(0);
Node* v = assembler->Parameter(1);
Node* context = assembler->Parameter(4);
Node* result = assembler->OrdinaryHasInstance(context, f, v);
assembler->Return(result);
}
// -----------------------------------------------------------------------------
// ES6 section 25.3 Generator Objects
@ -4218,7 +4233,6 @@ BUILTIN(FunctionPrototypeBind) {
return *function;
}
// ES6 section 19.2.3.5 Function.prototype.toString ( )
BUILTIN(FunctionPrototypeToString) {
HandleScope scope(isolate);

View File

@ -283,7 +283,6 @@ inline bool operator&(BuiltinExtraArguments lhs, BuiltinExtraArguments rhs) {
V(DatePrototypeGetUTCMonth, BUILTIN, UNINITIALIZED, kNoExtraICState) \
V(DatePrototypeGetUTCSeconds, BUILTIN, UNINITIALIZED, kNoExtraICState) \
\
V(FunctionHasInstance, BUILTIN, UNINITIALIZED, kNoExtraICState) \
V(FunctionPrototypeApply, BUILTIN, UNINITIALIZED, kNoExtraICState) \
V(FunctionPrototypeCall, BUILTIN, UNINITIALIZED, kNoExtraICState) \
\
@ -312,21 +311,22 @@ inline bool operator&(BuiltinExtraArguments lhs, BuiltinExtraArguments rhs) {
CODE_AGE_LIST_WITH_ARG(DECLARE_CODE_AGE_BUILTIN, V)
// Define list of builtins implemented in TurboFan (with JS linkage).
#define BUILTIN_LIST_T(V) \
V(GeneratorPrototypeNext, 2) \
V(GeneratorPrototypeReturn, 2) \
V(GeneratorPrototypeThrow, 2) \
V(MathCeil, 2) \
V(MathClz32, 2) \
V(MathFloor, 2) \
V(MathRound, 2) \
V(MathSqrt, 2) \
V(MathTrunc, 2) \
V(ObjectHasOwnProperty, 2) \
V(ArrayIsArray, 2) \
V(StringPrototypeCharAt, 2) \
V(StringPrototypeCharCodeAt, 2) \
V(AtomicsLoad, 3) \
#define BUILTIN_LIST_T(V) \
V(FunctionPrototypeHasInstance, 2) \
V(GeneratorPrototypeNext, 2) \
V(GeneratorPrototypeReturn, 2) \
V(GeneratorPrototypeThrow, 2) \
V(MathCeil, 2) \
V(MathClz32, 2) \
V(MathFloor, 2) \
V(MathRound, 2) \
V(MathSqrt, 2) \
V(MathTrunc, 2) \
V(ObjectHasOwnProperty, 2) \
V(ArrayIsArray, 2) \
V(StringPrototypeCharAt, 2) \
V(StringPrototypeCharCodeAt, 2) \
V(AtomicsLoad, 3) \
V(AtomicsStore, 4)
// Define list of builtin handlers implemented in assembly.
@ -594,7 +594,6 @@ class Builtins {
// ES6 section 20.3.4.19 Date.prototype.getUTCSeconds ( )
static void Generate_DatePrototypeGetUTCSeconds(MacroAssembler* masm);
static void Generate_FunctionHasInstance(MacroAssembler* masm);
static void Generate_FunctionPrototypeApply(MacroAssembler* masm);
static void Generate_FunctionPrototypeCall(MacroAssembler* masm);
@ -632,6 +631,10 @@ class Builtins {
// ES6 section 20.1.1.1 Number ( [ value ] ) for the [[Construct]] case.
static void Generate_NumberConstructor_ConstructStub(MacroAssembler* masm);
// ES6 section 19.2.3.6 Function.prototype [ @@hasInstance ] ( V )
static void Generate_FunctionPrototypeHasInstance(
CodeStubAssembler* assembler);
// ES6 section 25.3.1.2 Generator.prototype.next ( value )
static void Generate_GeneratorPrototypeNext(CodeStubAssembler* assembler);
// ES6 section 25.3.1.3 Generator.prototype.return ( value )

View File

@ -1416,5 +1416,141 @@ void CodeStubAssembler::TryLookupElement(Node* object, Node* map,
}
}
Node* CodeStubAssembler::OrdinaryHasInstance(Node* context, Node* callable,
Node* object) {
Variable var_result(this, MachineRepresentation::kTagged);
Label return_false(this), return_true(this),
return_runtime(this, Label::kDeferred), return_result(this);
// Goto runtime if {object} is a Smi.
GotoIf(WordIsSmi(object), &return_runtime);
// Load map of {object}.
Node* object_map = LoadMap(object);
// Lookup the {callable} and {object} map in the global instanceof cache.
// Note: This is safe because we clear the global instanceof cache whenever
// we change the prototype of any object.
Node* instanceof_cache_function =
LoadRoot(Heap::kInstanceofCacheFunctionRootIndex);
Node* instanceof_cache_map = LoadRoot(Heap::kInstanceofCacheMapRootIndex);
{
Label instanceof_cache_miss(this);
GotoUnless(WordEqual(instanceof_cache_function, callable),
&instanceof_cache_miss);
GotoUnless(WordEqual(instanceof_cache_map, object_map),
&instanceof_cache_miss);
var_result.Bind(LoadRoot(Heap::kInstanceofCacheAnswerRootIndex));
Goto(&return_result);
Bind(&instanceof_cache_miss);
}
// Goto runtime if {callable} is a Smi.
GotoIf(WordIsSmi(callable), &return_runtime);
// Load map of {callable}.
Node* callable_map = LoadMap(callable);
// Goto runtime if {callable} is not a JSFunction.
Node* callable_instance_type = LoadMapInstanceType(callable_map);
GotoUnless(
Word32Equal(callable_instance_type, Int32Constant(JS_FUNCTION_TYPE)),
&return_runtime);
// Goto runtime if {callable} is not a constructor or has
// a non-instance "prototype".
Node* callable_bitfield = LoadMapBitField(callable_map);
GotoUnless(
Word32Equal(Word32And(callable_bitfield,
Int32Constant((1 << Map::kHasNonInstancePrototype) |
(1 << Map::kIsConstructor))),
Int32Constant(1 << Map::kIsConstructor)),
&return_runtime);
// Get the "prototype" (or initial map) of the {callable}.
Node* callable_prototype =
LoadObjectField(callable, JSFunction::kPrototypeOrInitialMapOffset);
{
Variable var_callable_prototype(this, MachineRepresentation::kTagged);
Label callable_prototype_valid(this);
var_callable_prototype.Bind(callable_prototype);
// Resolve the "prototype" if the {callable} has an initial map. Afterwards
// the {callable_prototype} will be either the JSReceiver prototype object
// or the hole value, which means that no instances of the {callable} were
// created so far and hence we should return false.
Node* callable_prototype_instance_type =
LoadInstanceType(callable_prototype);
GotoUnless(
Word32Equal(callable_prototype_instance_type, Int32Constant(MAP_TYPE)),
&callable_prototype_valid);
var_callable_prototype.Bind(
LoadObjectField(callable_prototype, Map::kPrototypeOffset));
Goto(&callable_prototype_valid);
Bind(&callable_prototype_valid);
callable_prototype = var_callable_prototype.value();
}
// Update the global instanceof cache with the current {object} map and
// {callable}. The cached answer will be set when it is known below.
StoreRoot(Heap::kInstanceofCacheFunctionRootIndex, callable);
StoreRoot(Heap::kInstanceofCacheMapRootIndex, object_map);
// Loop through the prototype chain looking for the {callable} prototype.
Variable var_object_map(this, MachineRepresentation::kTagged);
var_object_map.Bind(object_map);
Label loop(this, &var_object_map);
Goto(&loop);
Bind(&loop);
{
Node* object_map = var_object_map.value();
// Check if the current {object} needs to be access checked.
Node* object_bitfield = LoadMapBitField(object_map);
GotoUnless(
Word32Equal(Word32And(object_bitfield,
Int32Constant(1 << Map::kIsAccessCheckNeeded)),
Int32Constant(0)),
&return_runtime);
// Check if the current {object} is a proxy.
Node* object_instance_type = LoadMapInstanceType(object_map);
GotoIf(Word32Equal(object_instance_type, Int32Constant(JS_PROXY_TYPE)),
&return_runtime);
// Check the current {object} prototype.
Node* object_prototype = LoadMapPrototype(object_map);
GotoIf(WordEqual(object_prototype, callable_prototype), &return_true);
GotoIf(WordEqual(object_prototype, NullConstant()), &return_false);
// Continue with the prototype.
var_object_map.Bind(LoadMap(object_prototype));
Goto(&loop);
}
Bind(&return_true);
StoreRoot(Heap::kInstanceofCacheAnswerRootIndex, BooleanConstant(true));
var_result.Bind(BooleanConstant(true));
Goto(&return_result);
Bind(&return_false);
StoreRoot(Heap::kInstanceofCacheAnswerRootIndex, BooleanConstant(false));
var_result.Bind(BooleanConstant(false));
Goto(&return_result);
Bind(&return_runtime);
{
// Invalidate the global instanceof cache.
StoreRoot(Heap::kInstanceofCacheFunctionRootIndex, SmiConstant(0));
// Fallback to the runtime implementation.
var_result.Bind(
CallRuntime(Runtime::kOrdinaryHasInstance, context, callable, object));
}
Goto(&return_result);
Bind(&return_result);
return var_result.value();
}
} // namespace internal
} // namespace v8

View File

@ -245,6 +245,12 @@ class CodeStubAssembler : public compiler::CodeAssembler {
Label* if_found, Label* if_not_found,
Label* call_runtime);
// Instanceof helpers.
// ES6 section 7.3.19 OrdinaryHasInstance (C, O)
compiler::Node* OrdinaryHasInstance(compiler::Node* context,
compiler::Node* callable,
compiler::Node* object);
private:
compiler::Node* AllocateRawAligned(compiler::Node* size_in_bytes,
AllocationFlags flags,

View File

@ -1759,6 +1759,43 @@ void DecStub::GenerateAssembly(CodeStubAssembler* assembler) const {
}
}
void InstanceOfStub::GenerateAssembly(CodeStubAssembler* assembler) const {
typedef CodeStubAssembler::Label Label;
typedef compiler::Node Node;
Node* object = assembler->Parameter(0);
Node* callable = assembler->Parameter(1);
Node* context = assembler->Parameter(2);
Label return_runtime(assembler, Label::kDeferred);
// Check if no one installed @@hasInstance somewhere.
assembler->GotoUnless(
assembler->WordEqual(
assembler->LoadObjectField(
assembler->LoadRoot(Heap::kHasInstanceProtectorRootIndex),
PropertyCell::kValueOffset),
assembler->SmiConstant(Smi::FromInt(Isolate::kArrayProtectorValid))),
&return_runtime);
// Check if {callable} is a valid receiver.
assembler->GotoIf(assembler->WordIsSmi(callable), &return_runtime);
assembler->GotoIf(
assembler->Word32Equal(
assembler->Word32And(
assembler->LoadMapBitField(assembler->LoadMap(callable)),
assembler->Int32Constant(1 << Map::kIsCallable)),
assembler->Int32Constant(0)),
&return_runtime);
// Use the inline OrdinaryHasInstance directly.
assembler->Return(assembler->OrdinaryHasInstance(context, callable, object));
// TODO(bmeurer): Use GetPropertyStub here once available.
assembler->Bind(&return_runtime);
assembler->TailCallRuntime(Runtime::kInstanceOf, context, object, callable);
}
namespace {
enum RelationalComparisonMode {

View File

@ -31,7 +31,6 @@ namespace internal {
V(CompareIC) \
V(DoubleToI) \
V(FunctionPrototype) \
V(InstanceOf) \
V(InternalArrayConstructor) \
V(JSEntry) \
V(KeyedLoadICTrampoline) \
@ -121,6 +120,7 @@ namespace internal {
V(InternalArrayNoArgumentConstructor) \
V(Dec) \
V(FastCloneShallowObject) \
V(InstanceOf) \
V(LessThan) \
V(LessThanOrEqual) \
V(GreaterThan) \
@ -781,6 +781,15 @@ class DecStub final : public TurboFanCodeStub {
DEFINE_TURBOFAN_CODE_STUB(Dec, TurboFanCodeStub);
};
class InstanceOfStub final : public TurboFanCodeStub {
public:
explicit InstanceOfStub(Isolate* isolate) : TurboFanCodeStub(isolate) {}
private:
DEFINE_CALL_INTERFACE_DESCRIPTOR(Compare);
DEFINE_TURBOFAN_CODE_STUB(InstanceOf, TurboFanCodeStub);
};
class LessThanStub final : public TurboFanCodeStub {
public:
explicit LessThanStub(Isolate* isolate) : TurboFanCodeStub(isolate) {}
@ -1238,24 +1247,6 @@ class FastArrayPushStub : public HydrogenCodeStub {
DEFINE_HYDROGEN_CODE_STUB(FastArrayPush, HydrogenCodeStub);
};
class InstanceOfStub final : public PlatformCodeStub {
public:
explicit InstanceOfStub(Isolate* isolate, bool es6_instanceof = false)
: PlatformCodeStub(isolate) {
minor_key_ = IsES6InstanceOfBits::encode(es6_instanceof);
}
bool is_es6_instanceof() const {
return IsES6InstanceOfBits::decode(minor_key_);
}
private:
class IsES6InstanceOfBits : public BitField<bool, 0, 1> {};
DEFINE_CALL_INTERFACE_DESCRIPTOR(InstanceOf);
DEFINE_PLATFORM_CODE_STUB(InstanceOf, PlatformCodeStub);
};
enum AllocationSiteOverrideMode {
DONT_OVERRIDE,

View File

@ -2901,7 +2901,6 @@ void AstGraphBuilder::VisitCompareOperation(CompareOperation* expr) {
op = javascript()->GreaterThanOrEqual();
break;
case Token::INSTANCEOF:
DCHECK(!FLAG_harmony_instanceof);
op = javascript()->InstanceOf();
break;
case Token::IN:

View File

@ -1213,7 +1213,6 @@ void BytecodeGraphBuilder::VisitTestIn() {
}
void BytecodeGraphBuilder::VisitTestInstanceOf() {
DCHECK(!FLAG_harmony_instanceof);
BuildCompareOp(javascript()->InstanceOf());
}

View File

@ -205,14 +205,10 @@ Node* CodeAssembler::LoadRoot(Heap::RootListIndex root_index) {
}
}
compiler::Node* roots_array_start =
Node* roots_array_start =
ExternalConstant(ExternalReference::roots_array_start(isolate()));
USE(roots_array_start);
// TODO(danno): Implement the root-access case where the root is not constant
// and must be loaded from the root array.
UNIMPLEMENTED();
return nullptr;
return Load(MachineType::AnyTagged(), roots_array_start,
IntPtrConstant(root_index * kPointerSize));
}
Node* CodeAssembler::Store(MachineRepresentation rep, Node* base, Node* value) {
@ -239,6 +235,14 @@ Node* CodeAssembler::AtomicStore(MachineRepresentation rep, Node* base,
return raw_assembler_->AtomicStore(rep, base, index, value);
}
Node* CodeAssembler::StoreRoot(Heap::RootListIndex root_index, Node* value) {
DCHECK(Heap::RootCanBeWrittenAfterInitialization(root_index));
Node* roots_array_start =
ExternalConstant(ExternalReference::roots_array_start(isolate()));
return StoreNoWriteBarrier(MachineRepresentation::kTagged, roots_array_start,
IntPtrConstant(root_index * kPointerSize), value);
}
Node* CodeAssembler::Projection(int index, Node* value) {
return raw_assembler_->Projection(index, value);
}

View File

@ -236,6 +236,9 @@ class CodeAssembler {
Node* AtomicStore(MachineRepresentation rep, Node* base, Node* index,
Node* value);
// Store a value to the root array.
Node* StoreRoot(Heap::RootListIndex root_index, Node* value);
// Basic arithmetic operations.
#define DECLARE_CODE_ASSEMBLER_BINARY_OP(name) Node* name(Node* a, Node* b);
CODE_ASSEMBLER_BINARY_OP_LIST(DECLARE_CODE_ASSEMBLER_BINARY_OP)

View File

@ -86,8 +86,6 @@ Reduction JSIntrinsicLowering::Reduce(Node* node) {
return ReduceNewObject(node);
case Runtime::kInlineGetSuperConstructor:
return ReduceGetSuperConstructor(node);
case Runtime::kInlineGetOrdinaryHasInstance:
return ReduceGetOrdinaryHasInstance(node);
default:
break;
}
@ -411,17 +409,6 @@ Reduction JSIntrinsicLowering::ReduceGetSuperConstructor(Node* node) {
active_function_map, effect, control);
}
Reduction JSIntrinsicLowering::ReduceGetOrdinaryHasInstance(Node* node) {
Node* effect = NodeProperties::GetEffectInput(node);
Node* context = NodeProperties::GetContextInput(node);
Node* native_context = effect = graph()->NewNode(
javascript()->LoadContext(0, Context::NATIVE_CONTEXT_INDEX, true),
context, context, effect);
return Change(node, javascript()->LoadContext(
0, Context::ORDINARY_HAS_INSTANCE_INDEX, true),
native_context, context, effect);
}
Reduction JSIntrinsicLowering::Change(Node* node, const Operator* op, Node* a,
Node* b) {
RelaxControls(node);

View File

@ -63,7 +63,6 @@ class JSIntrinsicLowering final : public AdvancedReducer {
Reduction ReduceCall(Node* node);
Reduction ReduceNewObject(Node* node);
Reduction ReduceGetSuperConstructor(Node* node);
Reduction ReduceGetOrdinaryHasInstance(Node* node);
Reduction Change(Node* node, const Operator* op);
Reduction Change(Node* node, const Operator* op, Node* a, Node* b);

View File

@ -1066,10 +1066,7 @@ Reduction JSTypedLowering::ReduceJSInstanceOf(Node* node) {
Node* const frame_state = NodeProperties::GetFrameStateInput(node, 0);
// If deoptimization is disabled, we cannot optimize.
if (!(flags() & kDeoptimizationEnabled) ||
(flags() & kDisableBinaryOpReduction)) {
return NoChange();
}
if (!(flags() & kDeoptimizationEnabled)) return NoChange();
// If we are in a try block, don't optimize since the runtime call
// in the proxy case can throw.
@ -1088,15 +1085,21 @@ Reduction JSTypedLowering::ReduceJSInstanceOf(Node* node) {
Handle<JSFunction>::cast(r.right_type()->AsConstant()->Value());
Handle<SharedFunctionInfo> shared(function->shared(), isolate());
if (!function->IsConstructor() ||
function->map()->has_non_instance_prototype()) {
// Make sure the prototype of {function} is the %FunctionPrototype%, and it
// already has a meaningful initial map (i.e. we constructed at least one
// instance using the constructor {function}).
if (function->map()->prototype() != function->native_context()->closure() ||
function->map()->has_non_instance_prototype() ||
!function->has_initial_map()) {
return NoChange();
}
JSFunction::EnsureHasInitialMap(function);
DCHECK(function->has_initial_map());
// We can only use the fast case if @@hasInstance was not used so far.
if (!isolate()->IsHasInstanceLookupChainIntact()) return NoChange();
dependencies()->AssumePropertyCell(factory()->has_instance_protector());
Handle<Map> initial_map(function->initial_map(), isolate());
this->dependencies()->AssumeInitialMapCantChange(initial_map);
dependencies()->AssumeInitialMapCantChange(initial_map);
Node* prototype =
jsgraph()->Constant(handle(initial_map->prototype(), isolate()));

View File

@ -86,7 +86,6 @@ enum BindingFlags {
V(REFLECT_DELETE_PROPERTY_INDEX, JSFunction, reflect_delete_property) \
V(SPREAD_ARGUMENTS_INDEX, JSFunction, spread_arguments) \
V(SPREAD_ITERABLE_INDEX, JSFunction, spread_iterable) \
V(ORDINARY_HAS_INSTANCE_INDEX, JSFunction, ordinary_has_instance) \
V(MATH_FLOOR, JSFunction, math_floor) \
V(MATH_SQRT, JSFunction, math_sqrt)

View File

@ -934,17 +934,6 @@ LInstruction* LChunkBuilder::DoArgumentsElements(HArgumentsElements* elems) {
}
LInstruction* LChunkBuilder::DoInstanceOf(HInstanceOf* instr) {
LOperand* left =
UseFixed(instr->left(), InstanceOfDescriptor::LeftRegister());
LOperand* right =
UseFixed(instr->right(), InstanceOfDescriptor::RightRegister());
LOperand* context = UseFixed(instr->context(), cp);
LInstanceOf* result = new (zone()) LInstanceOf(context, left, right);
return MarkAsCall(DefineFixed(result, r0), instr);
}
LInstruction* LChunkBuilder::DoHasInPrototypeChainAndBranch(
HHasInPrototypeChainAndBranch* instr) {
LOperand* object = UseRegister(instr->object());

View File

@ -79,7 +79,6 @@ class LCodeGen;
V(HasInPrototypeChainAndBranch) \
V(HasInstanceTypeAndBranch) \
V(InnerAllocatedObject) \
V(InstanceOf) \
V(InstructionGap) \
V(Integer32ToDouble) \
V(InvokeFunction) \
@ -1135,22 +1134,6 @@ class LCmpT final : public LTemplateInstruction<1, 3, 0> {
};
class LInstanceOf final : public LTemplateInstruction<1, 3, 0> {
public:
LInstanceOf(LOperand* context, LOperand* left, LOperand* right) {
inputs_[0] = context;
inputs_[1] = left;
inputs_[2] = right;
}
LOperand* context() const { return inputs_[0]; }
LOperand* left() const { return inputs_[1]; }
LOperand* right() const { return inputs_[2]; }
DECLARE_CONCRETE_INSTRUCTION(InstanceOf, "instance-of")
};
class LHasInPrototypeChainAndBranch final : public LControlInstruction<2, 0> {
public:
LHasInPrototypeChainAndBranch(LOperand* object, LOperand* prototype) {

View File

@ -2494,16 +2494,6 @@ void LCodeGen::DoCmpMapAndBranch(LCmpMapAndBranch* instr) {
}
void LCodeGen::DoInstanceOf(LInstanceOf* instr) {
DCHECK(ToRegister(instr->context()).is(cp));
DCHECK(ToRegister(instr->left()).is(InstanceOfDescriptor::LeftRegister()));
DCHECK(ToRegister(instr->right()).is(InstanceOfDescriptor::RightRegister()));
DCHECK(ToRegister(instr->result()).is(r0));
InstanceOfStub stub(isolate());
CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
}
void LCodeGen::DoHasInPrototypeChainAndBranch(
LHasInPrototypeChainAndBranch* instr) {
Register const object = ToRegister(instr->object());

View File

@ -1467,17 +1467,6 @@ LInstruction* LChunkBuilder::DoInnerAllocatedObject(
}
LInstruction* LChunkBuilder::DoInstanceOf(HInstanceOf* instr) {
LOperand* left =
UseFixed(instr->left(), InstanceOfDescriptor::LeftRegister());
LOperand* right =
UseFixed(instr->right(), InstanceOfDescriptor::RightRegister());
LOperand* context = UseFixed(instr->context(), cp);
LInstanceOf* result = new (zone()) LInstanceOf(context, left, right);
return MarkAsCall(DefineFixed(result, x0), instr);
}
LInstruction* LChunkBuilder::DoHasInPrototypeChainAndBranch(
HHasInPrototypeChainAndBranch* instr) {
LOperand* object = UseRegister(instr->object());

View File

@ -82,7 +82,6 @@ class LCodeGen;
V(HasInPrototypeChainAndBranch) \
V(HasInstanceTypeAndBranch) \
V(InnerAllocatedObject) \
V(InstanceOf) \
V(InstructionGap) \
V(Integer32ToDouble) \
V(InvokeFunction) \
@ -1378,22 +1377,6 @@ class LInnerAllocatedObject final : public LTemplateInstruction<1, 2, 0> {
};
class LInstanceOf final : public LTemplateInstruction<1, 3, 0> {
public:
LInstanceOf(LOperand* context, LOperand* left, LOperand* right) {
inputs_[0] = context;
inputs_[1] = left;
inputs_[2] = right;
}
LOperand* context() const { return inputs_[0]; }
LOperand* left() const { return inputs_[1]; }
LOperand* right() const { return inputs_[2]; }
DECLARE_CONCRETE_INSTRUCTION(InstanceOf, "instance-of")
};
class LHasInPrototypeChainAndBranch final : public LControlInstruction<2, 2> {
public:
LHasInPrototypeChainAndBranch(LOperand* object, LOperand* prototype,

View File

@ -2802,16 +2802,6 @@ void LCodeGen::DoInnerAllocatedObject(LInnerAllocatedObject* instr) {
}
void LCodeGen::DoInstanceOf(LInstanceOf* instr) {
DCHECK(ToRegister(instr->context()).is(cp));
DCHECK(ToRegister(instr->left()).is(InstanceOfDescriptor::LeftRegister()));
DCHECK(ToRegister(instr->right()).is(InstanceOfDescriptor::RightRegister()));
DCHECK(ToRegister(instr->result()).is(x0));
InstanceOfStub stub(isolate());
CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
}
void LCodeGen::DoHasInPrototypeChainAndBranch(
LHasInPrototypeChainAndBranch* instr) {
Register const object = ToRegister(instr->object());

View File

@ -798,7 +798,6 @@ bool HInstruction::CanDeoptimize() {
case HValue::kHasCachedArrayIndexAndBranch:
case HValue::kHasInstanceTypeAndBranch:
case HValue::kInnerAllocatedObject:
case HValue::kInstanceOf:
case HValue::kIsSmiAndBranch:
case HValue::kIsStringAndBranch:
case HValue::kIsUndetectableAndBranch:
@ -1652,12 +1651,6 @@ std::ostream& HUnknownOSRValue::PrintDataTo(std::ostream& os) const { // NOLINT
}
std::ostream& HInstanceOf::PrintDataTo(std::ostream& os) const { // NOLINT
return os << NameOf(left()) << " " << NameOf(right()) << " "
<< NameOf(context());
}
Range* HValue::InferRange(Zone* zone) {
Range* result;
if (representation().IsSmi() || type().IsSmi()) {

View File

@ -95,7 +95,6 @@ class LChunkBuilder;
V(HasCachedArrayIndexAndBranch) \
V(HasInstanceTypeAndBranch) \
V(InnerAllocatedObject) \
V(InstanceOf) \
V(InvokeFunction) \
V(HasInPrototypeChainAndBranch) \
V(IsStringAndBranch) \
@ -4271,27 +4270,6 @@ class HTypeofIsAndBranch final : public HUnaryControlInstruction {
};
class HInstanceOf final : public HBinaryOperation {
public:
DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P2(HInstanceOf, HValue*, HValue*);
Representation RequiredInputRepresentation(int index) override {
return Representation::Tagged();
}
std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT
DECLARE_CONCRETE_INSTRUCTION(InstanceOf)
private:
HInstanceOf(HValue* context, HValue* left, HValue* right)
: HBinaryOperation(context, left, right, HType::Boolean()) {
set_representation(Representation::Tagged());
SetAllSideEffects();
}
};
class HHasInPrototypeChainAndBranch final
: public HTemplateControlInstruction<2, 2> {
public:

View File

@ -11680,18 +11680,24 @@ void HOptimizedGraphBuilder::VisitCompareOperation(CompareOperation* expr) {
}
if (op == Token::INSTANCEOF) {
DCHECK(!FLAG_harmony_instanceof);
// Check to see if the rhs of the instanceof is a known function.
if (right->IsConstant() &&
HConstant::cast(right)->handle(isolate())->IsJSFunction()) {
Handle<JSFunction> constructor =
Handle<JSFunction> function =
Handle<JSFunction>::cast(HConstant::cast(right)->handle(isolate()));
if (constructor->IsConstructor() &&
!constructor->map()->has_non_instance_prototype()) {
JSFunction::EnsureHasInitialMap(constructor);
DCHECK(constructor->has_initial_map());
Handle<Map> initial_map(constructor->initial_map(), isolate());
// Make sure the prototype of {function} is the %FunctionPrototype%, and
// it already has a meaningful initial map (i.e. we constructed at least
// one instance using the constructor {function}).
// We can only use the fast case if @@hasInstance was not used so far.
if (function->has_initial_map() &&
function->map()->prototype() ==
function->native_context()->closure() &&
!function->map()->has_non_instance_prototype() &&
isolate()->IsHasInstanceLookupChainIntact()) {
Handle<Map> initial_map(function->initial_map(), isolate());
top_info()->dependencies()->AssumeInitialMapCantChange(initial_map);
top_info()->dependencies()->AssumePropertyCell(
isolate()->factory()->has_instance_protector());
HInstruction* prototype =
Add<HConstant>(handle(initial_map->prototype(), isolate()));
HHasInPrototypeChainAndBranch* result =
@ -11700,7 +11706,12 @@ void HOptimizedGraphBuilder::VisitCompareOperation(CompareOperation* expr) {
}
}
HInstanceOf* result = New<HInstanceOf>(left, right);
Callable callable = CodeFactory::InstanceOf(isolate());
HValue* stub = Add<HConstant>(callable.code());
HValue* values[] = {context(), left, right};
HCallWithDescriptor* result = New<HCallWithDescriptor>(
stub, 0, callable.descriptor(), ArrayVector(values));
result->set_type(HType::Boolean());
return ast_context()->ReturnInstruction(result, expr->id());
} else if (op == Token::IN) {
@ -12801,45 +12812,6 @@ void HOptimizedGraphBuilder::GenerateNumberToString(CallRuntime* call) {
void HOptimizedGraphBuilder::GenerateCall(CallRuntime* call) {
DCHECK_LE(2, call->arguments()->length());
CHECK_ALIVE(VisitExpressions(call->arguments()));
// Try and customize ES6 instanceof here.
// We should at least have the constructor on the expression stack.
if (FLAG_harmony_instanceof && FLAG_harmony_instanceof_opt &&
call->arguments()->length() == 3) {
HValue* target = environment()->ExpressionStackAt(2);
if (target->IsConstant()) {
HConstant* constant_function = HConstant::cast(target);
if (constant_function->handle(isolate())->IsJSFunction()) {
Handle<JSFunction> func =
Handle<JSFunction>::cast(constant_function->handle(isolate()));
if (*func == isolate()->native_context()->ordinary_has_instance()) {
// Look at the function, which will be argument 1.
HValue* right = environment()->ExpressionStackAt(1);
if (right->IsConstant() &&
HConstant::cast(right)->handle(isolate())->IsJSFunction()) {
Handle<JSFunction> constructor = Handle<JSFunction>::cast(
HConstant::cast(right)->handle(isolate()));
if (constructor->IsConstructor() &&
!constructor->map()->has_non_instance_prototype()) {
JSFunction::EnsureHasInitialMap(constructor);
DCHECK(constructor->has_initial_map());
Handle<Map> initial_map(constructor->initial_map(), isolate());
top_info()->dependencies()->AssumeInitialMapCantChange(
initial_map);
HInstruction* prototype =
Add<HConstant>(handle(initial_map->prototype(), isolate()));
HValue* left = environment()->ExpressionStackAt(0);
HHasInPrototypeChainAndBranch* result =
New<HHasInPrototypeChainAndBranch>(left, prototype);
Drop(3);
return ast_context()->ReturnControl(result, call->id());
}
}
}
}
}
}
CallTrampolineDescriptor descriptor(isolate());
PushArgumentsFromEnvironment(call->arguments()->length() - 1);
HValue* trampoline = Add<HConstant>(isolate()->builtins()->Call());
@ -13080,13 +13052,6 @@ void HOptimizedGraphBuilder::GenerateDebugIsActive(CallRuntime* call) {
return ast_context()->ReturnValue(value);
}
void HOptimizedGraphBuilder::GenerateGetOrdinaryHasInstance(CallRuntime* call) {
DCHECK(call->arguments()->length() == 0);
// ordinary_has_instance is immutable so we can treat it as a constant.
HValue* value = Add<HConstant>(isolate()->ordinary_has_instance());
return ast_context()->ReturnValue(value);
}
#undef CHECK_BAILOUT
#undef CHECK_ALIVE

View File

@ -2286,7 +2286,6 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor {
F(RegExpSource) \
F(NumberToString) \
F(DebugIsActive) \
F(GetOrdinaryHasInstance) \
/* Typed Arrays */ \
F(TypedArrayInitialize) \
F(MaxSmi) \

View File

@ -2287,16 +2287,6 @@ void LCodeGen::DoCmpMapAndBranch(LCmpMapAndBranch* instr) {
}
void LCodeGen::DoInstanceOf(LInstanceOf* instr) {
DCHECK(ToRegister(instr->context()).is(esi));
DCHECK(ToRegister(instr->left()).is(InstanceOfDescriptor::LeftRegister()));
DCHECK(ToRegister(instr->right()).is(InstanceOfDescriptor::RightRegister()));
DCHECK(ToRegister(instr->result()).is(eax));
InstanceOfStub stub(isolate());
CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
}
void LCodeGen::DoHasInPrototypeChainAndBranch(
LHasInPrototypeChainAndBranch* instr) {
Register const object = ToRegister(instr->object());

View File

@ -966,17 +966,6 @@ LInstruction* LChunkBuilder::DoArgumentsElements(HArgumentsElements* elems) {
}
LInstruction* LChunkBuilder::DoInstanceOf(HInstanceOf* instr) {
LOperand* left =
UseFixed(instr->left(), InstanceOfDescriptor::LeftRegister());
LOperand* right =
UseFixed(instr->right(), InstanceOfDescriptor::RightRegister());
LOperand* context = UseFixed(instr->context(), esi);
LInstanceOf* result = new (zone()) LInstanceOf(context, left, right);
return MarkAsCall(DefineFixed(result, eax), instr);
}
LInstruction* LChunkBuilder::DoHasInPrototypeChainAndBranch(
HHasInPrototypeChainAndBranch* instr) {
LOperand* object = UseRegister(instr->object());

View File

@ -83,7 +83,6 @@ class LCodeGen;
V(HasInPrototypeChainAndBranch) \
V(HasInstanceTypeAndBranch) \
V(InnerAllocatedObject) \
V(InstanceOf) \
V(InstructionGap) \
V(Integer32ToDouble) \
V(InvokeFunction) \
@ -1140,22 +1139,6 @@ class LCmpT final : public LTemplateInstruction<1, 3, 0> {
};
class LInstanceOf final : public LTemplateInstruction<1, 3, 0> {
public:
LInstanceOf(LOperand* context, LOperand* left, LOperand* right) {
inputs_[0] = context;
inputs_[1] = left;
inputs_[2] = right;
}
LOperand* context() const { return inputs_[0]; }
LOperand* left() const { return inputs_[1]; }
LOperand* right() const { return inputs_[2]; }
DECLARE_CONCRETE_INSTRUCTION(InstanceOf, "instance-of")
};
class LHasInPrototypeChainAndBranch final : public LControlInstruction<2, 1> {
public:
LHasInPrototypeChainAndBranch(LOperand* object, LOperand* prototype,

View File

@ -2381,16 +2381,6 @@ void LCodeGen::DoCmpMapAndBranch(LCmpMapAndBranch* instr) {
}
void LCodeGen::DoInstanceOf(LInstanceOf* instr) {
DCHECK(ToRegister(instr->context()).is(cp));
DCHECK(ToRegister(instr->left()).is(InstanceOfDescriptor::LeftRegister()));
DCHECK(ToRegister(instr->right()).is(InstanceOfDescriptor::RightRegister()));
DCHECK(ToRegister(instr->result()).is(v0));
InstanceOfStub stub(isolate());
CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
}
void LCodeGen::DoHasInPrototypeChainAndBranch(
LHasInPrototypeChainAndBranch* instr) {
Register const object = ToRegister(instr->object());

View File

@ -939,17 +939,6 @@ LInstruction* LChunkBuilder::DoArgumentsElements(HArgumentsElements* elems) {
}
LInstruction* LChunkBuilder::DoInstanceOf(HInstanceOf* instr) {
LOperand* left =
UseFixed(instr->left(), InstanceOfDescriptor::LeftRegister());
LOperand* right =
UseFixed(instr->right(), InstanceOfDescriptor::RightRegister());
LOperand* context = UseFixed(instr->context(), cp);
LInstanceOf* result = new (zone()) LInstanceOf(context, left, right);
return MarkAsCall(DefineFixed(result, v0), instr);
}
LInstruction* LChunkBuilder::DoHasInPrototypeChainAndBranch(
HHasInPrototypeChainAndBranch* instr) {
LOperand* object = UseRegister(instr->object());

View File

@ -79,7 +79,6 @@ class LCodeGen;
V(HasInPrototypeChainAndBranch) \
V(HasInstanceTypeAndBranch) \
V(InnerAllocatedObject) \
V(InstanceOf) \
V(InstructionGap) \
V(Integer32ToDouble) \
V(InvokeFunction) \
@ -1114,22 +1113,6 @@ class LCmpT final : public LTemplateInstruction<1, 3, 0> {
};
class LInstanceOf final : public LTemplateInstruction<1, 3, 0> {
public:
LInstanceOf(LOperand* context, LOperand* left, LOperand* right) {
inputs_[0] = context;
inputs_[1] = left;
inputs_[2] = right;
}
LOperand* context() const { return inputs_[0]; }
LOperand* left() const { return inputs_[1]; }
LOperand* right() const { return inputs_[2]; }
DECLARE_CONCRETE_INSTRUCTION(InstanceOf, "instance-of")
};
class LHasInPrototypeChainAndBranch final : public LControlInstruction<2, 0> {
public:
LHasInPrototypeChainAndBranch(LOperand* object, LOperand* prototype) {

View File

@ -2501,18 +2501,6 @@ void LCodeGen::DoCmpMapAndBranch(LCmpMapAndBranch* instr) {
}
void LCodeGen::DoInstanceOf(LInstanceOf* instr) {
DCHECK(ToRegister(instr->context()).is(cp));
Label true_label, done;
DCHECK(ToRegister(instr->left()).is(InstanceOfDescriptor::LeftRegister()));
DCHECK(ToRegister(instr->right()).is(InstanceOfDescriptor::RightRegister()));
DCHECK(ToRegister(instr->result()).is(v0));
InstanceOfStub stub(isolate());
CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
}
void LCodeGen::DoHasInPrototypeChainAndBranch(
LHasInPrototypeChainAndBranch* instr) {
Register const object = ToRegister(instr->object());

View File

@ -939,17 +939,6 @@ LInstruction* LChunkBuilder::DoArgumentsElements(HArgumentsElements* elems) {
}
LInstruction* LChunkBuilder::DoInstanceOf(HInstanceOf* instr) {
LOperand* left =
UseFixed(instr->left(), InstanceOfDescriptor::LeftRegister());
LOperand* right =
UseFixed(instr->right(), InstanceOfDescriptor::RightRegister());
LOperand* context = UseFixed(instr->context(), cp);
LInstanceOf* result = new (zone()) LInstanceOf(context, left, right);
return MarkAsCall(DefineFixed(result, v0), instr);
}
LInstruction* LChunkBuilder::DoHasInPrototypeChainAndBranch(
HHasInPrototypeChainAndBranch* instr) {
LOperand* object = UseRegister(instr->object());

View File

@ -81,7 +81,6 @@ class LCodeGen;
V(HasInPrototypeChainAndBranch) \
V(HasInstanceTypeAndBranch) \
V(InnerAllocatedObject) \
V(InstanceOf) \
V(InstructionGap) \
V(Integer32ToDouble) \
V(InvokeFunction) \
@ -1132,22 +1131,6 @@ class LCmpT final : public LTemplateInstruction<1, 3, 0> {
};
class LInstanceOf final : public LTemplateInstruction<1, 3, 0> {
public:
LInstanceOf(LOperand* context, LOperand* left, LOperand* right) {
inputs_[0] = context;
inputs_[1] = left;
inputs_[2] = right;
}
LOperand* context() const { return inputs_[0]; }
LOperand* left() const { return inputs_[1]; }
LOperand* right() const { return inputs_[2]; }
DECLARE_CONCRETE_INSTRUCTION(InstanceOf, "instance-of")
};
class LHasInPrototypeChainAndBranch final : public LControlInstruction<2, 0> {
public:
LHasInPrototypeChainAndBranch(LOperand* object, LOperand* prototype) {

View File

@ -2437,16 +2437,6 @@ void LCodeGen::DoCmpMapAndBranch(LCmpMapAndBranch* instr) {
}
void LCodeGen::DoInstanceOf(LInstanceOf* instr) {
DCHECK(ToRegister(instr->context()).is(rsi));
DCHECK(ToRegister(instr->left()).is(InstanceOfDescriptor::LeftRegister()));
DCHECK(ToRegister(instr->right()).is(InstanceOfDescriptor::RightRegister()));
DCHECK(ToRegister(instr->result()).is(rax));
InstanceOfStub stub(isolate());
CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
}
void LCodeGen::DoHasInPrototypeChainAndBranch(
LHasInPrototypeChainAndBranch* instr) {
Register const object = ToRegister(instr->object());

View File

@ -957,17 +957,6 @@ LInstruction* LChunkBuilder::DoArgumentsElements(HArgumentsElements* elems) {
}
LInstruction* LChunkBuilder::DoInstanceOf(HInstanceOf* instr) {
LOperand* left =
UseFixed(instr->left(), InstanceOfDescriptor::LeftRegister());
LOperand* right =
UseFixed(instr->right(), InstanceOfDescriptor::RightRegister());
LOperand* context = UseFixed(instr->context(), rsi);
LInstanceOf* result = new (zone()) LInstanceOf(context, left, right);
return MarkAsCall(DefineFixed(result, rax), instr);
}
LInstruction* LChunkBuilder::DoHasInPrototypeChainAndBranch(
HHasInPrototypeChainAndBranch* instr) {
LOperand* object = UseRegister(instr->object());

View File

@ -79,7 +79,6 @@ class LCodeGen;
V(HasInPrototypeChainAndBranch) \
V(HasInstanceTypeAndBranch) \
V(InnerAllocatedObject) \
V(InstanceOf) \
V(InstructionGap) \
V(Integer32ToDouble) \
V(InvokeFunction) \
@ -1137,22 +1136,6 @@ class LCmpT final : public LTemplateInstruction<1, 3, 0> {
};
class LInstanceOf final : public LTemplateInstruction<1, 3, 0> {
public:
LInstanceOf(LOperand* context, LOperand* left, LOperand* right) {
inputs_[0] = context;
inputs_[1] = left;
inputs_[2] = right;
}
LOperand* context() { return inputs_[0]; }
LOperand* left() { return inputs_[1]; }
LOperand* right() { return inputs_[2]; }
DECLARE_CONCRETE_INSTRUCTION(InstanceOf, "instance-of")
};
class LHasInPrototypeChainAndBranch final : public LControlInstruction<2, 0> {
public:
LHasInPrototypeChainAndBranch(LOperand* object, LOperand* prototype) {

View File

@ -280,7 +280,6 @@ DEFINE_BOOL(track_fields, true, "track fields with only smi values")
DEFINE_BOOL(track_double_fields, true, "track fields with double values")
DEFINE_BOOL(track_heap_object_fields, true, "track fields with heap values")
DEFINE_BOOL(track_computed_fields, true, "track computed boilerplate fields")
DEFINE_BOOL(harmony_instanceof_opt, true, "optimize ES6 instanceof support")
DEFINE_IMPLICATION(track_double_fields, track_fields)
DEFINE_IMPLICATION(track_heap_object_fields, track_fields)
DEFINE_IMPLICATION(track_computed_fields, track_fields)

View File

@ -3130,12 +3130,6 @@ void FullCodeGenerator::EmitGetSuperConstructor(CallRuntime* expr) {
context()->Plug(r0);
}
void FullCodeGenerator::EmitGetOrdinaryHasInstance(CallRuntime* expr) {
DCHECK_EQ(0, expr->arguments()->length());
__ LoadNativeContextSlot(Context::ORDINARY_HAS_INSTANCE_INDEX, r0);
context()->Plug(r0);
}
void FullCodeGenerator::EmitDebugIsActive(CallRuntime* expr) {
DCHECK(expr->arguments()->length() == 0);
ExternalReference debug_is_active =

View File

@ -3035,12 +3035,6 @@ void FullCodeGenerator::EmitGetSuperConstructor(CallRuntime* expr) {
context()->Plug(x0);
}
void FullCodeGenerator::EmitGetOrdinaryHasInstance(CallRuntime* expr) {
DCHECK_EQ(0, expr->arguments()->length());
__ LoadNativeContextSlot(Context::ORDINARY_HAS_INSTANCE_INDEX, x0);
context()->Plug(x0);
}
void FullCodeGenerator::EmitDebugIsActive(CallRuntime* expr) {
DCHECK(expr->arguments()->length() == 0);
ExternalReference debug_is_active =

View File

@ -545,7 +545,6 @@ class FullCodeGenerator: public AstVisitor {
F(ToName) \
F(ToObject) \
F(DebugIsActive) \
F(GetOrdinaryHasInstance) \
F(CreateIterResultObject)
#define GENERATOR_DECLARATION(Name) void Emit##Name(CallRuntime* call);

View File

@ -3029,13 +3029,6 @@ void FullCodeGenerator::EmitGetSuperConstructor(CallRuntime* expr) {
context()->Plug(eax);
}
void FullCodeGenerator::EmitGetOrdinaryHasInstance(CallRuntime* expr) {
DCHECK_EQ(0, expr->arguments()->length());
__ mov(eax, NativeContextOperand());
__ mov(eax, ContextOperand(eax, Context::ORDINARY_HAS_INSTANCE_INDEX));
context()->Plug(eax);
}
void FullCodeGenerator::EmitDebugIsActive(CallRuntime* expr) {
DCHECK(expr->arguments()->length() == 0);
ExternalReference debug_is_active =

View File

@ -3150,12 +3150,6 @@ void FullCodeGenerator::EmitGetSuperConstructor(CallRuntime* expr) {
context()->Plug(v0);
}
void FullCodeGenerator::EmitGetOrdinaryHasInstance(CallRuntime* expr) {
DCHECK_EQ(0, expr->arguments()->length());
__ LoadNativeContextSlot(Context::ORDINARY_HAS_INSTANCE_INDEX, v0);
context()->Plug(v0);
}
void FullCodeGenerator::EmitDebugIsActive(CallRuntime* expr) {
DCHECK(expr->arguments()->length() == 0);
ExternalReference debug_is_active =

View File

@ -3150,12 +3150,6 @@ void FullCodeGenerator::EmitGetSuperConstructor(CallRuntime* expr) {
context()->Plug(v0);
}
void FullCodeGenerator::EmitGetOrdinaryHasInstance(CallRuntime* expr) {
DCHECK_EQ(0, expr->arguments()->length());
__ LoadNativeContextSlot(Context::ORDINARY_HAS_INSTANCE_INDEX, v0);
context()->Plug(v0);
}
void FullCodeGenerator::EmitDebugIsActive(CallRuntime* expr) {
DCHECK(expr->arguments()->length() == 0);
ExternalReference debug_is_active =

View File

@ -3015,12 +3015,6 @@ void FullCodeGenerator::EmitGetSuperConstructor(CallRuntime* expr) {
context()->Plug(rax);
}
void FullCodeGenerator::EmitGetOrdinaryHasInstance(CallRuntime* expr) {
DCHECK_EQ(0, expr->arguments()->length());
__ LoadNativeContextSlot(Context::ORDINARY_HAS_INSTANCE_INDEX, rax);
context()->Plug(rax);
}
void FullCodeGenerator::EmitDebugIsActive(CallRuntime* expr) {
DCHECK(expr->arguments()->length() == 0);
ExternalReference debug_is_active =

View File

@ -2839,6 +2839,10 @@ void Heap::CreateInitialObjects() {
cell->set_value(the_hole_value());
set_empty_property_cell(*cell);
cell = factory->NewPropertyCell();
cell->set_value(Smi::FromInt(Isolate::kArrayProtectorValid));
set_has_instance_protector(*cell);
Handle<Cell> is_concat_spreadable_cell = factory->NewCell(
handle(Smi::FromInt(Isolate::kArrayProtectorValid), isolate()));
set_is_concat_spreadable_protector(*is_concat_spreadable_cell);

View File

@ -194,6 +194,7 @@ using v8::MemoryPressureLevel;
V(Object, noscript_shared_function_infos, NoScriptSharedFunctionInfos) \
V(Map, bytecode_array_map, BytecodeArrayMap) \
V(WeakCell, empty_weak_cell, EmptyWeakCell) \
V(PropertyCell, has_instance_protector, HasInstanceProtector) \
V(Cell, species_protector, SpeciesProtector)
// Entries in this list are limited to Smis and are not visited during GC.

View File

@ -1212,29 +1212,6 @@ void Builtins::Generate_DatePrototype_GetField(MacroAssembler* masm,
}
}
// static
void Builtins::Generate_FunctionHasInstance(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- eax : argc
// -- esp[0] : return address
// -- esp[4] : first argument (left-hand side)
// -- esp[8] : receiver (right-hand side)
// -----------------------------------
{
FrameScope scope(masm, StackFrame::INTERNAL);
__ mov(InstanceOfDescriptor::LeftRegister(),
Operand(ebp, 2 * kPointerSize)); // Load left-hand side.
__ mov(InstanceOfDescriptor::RightRegister(),
Operand(ebp, 3 * kPointerSize)); // Load right-hand side.
InstanceOfStub stub(masm->isolate(), true);
__ CallStub(&stub);
}
// Pop the argument and the receiver.
__ ret(2 * kPointerSize);
}
// static
void Builtins::Generate_FunctionPrototypeApply(MacroAssembler* masm) {
// ----------- S t a t e -------------

View File

@ -2046,129 +2046,6 @@ void JSEntryStub::Generate(MacroAssembler* masm) {
}
void InstanceOfStub::Generate(MacroAssembler* masm) {
Register const object = edx; // Object (lhs).
Register const function = eax; // Function (rhs).
Register const object_map = ecx; // Map of {object}.
Register const function_map = ebx; // Map of {function}.
Register const function_prototype = function_map; // Prototype of {function}.
Register const scratch = edi;
DCHECK(object.is(InstanceOfDescriptor::LeftRegister()));
DCHECK(function.is(InstanceOfDescriptor::RightRegister()));
// Check if {object} is a smi.
Label object_is_smi;
__ JumpIfSmi(object, &object_is_smi, Label::kNear);
// Lookup the {function} and the {object} map in the global instanceof cache.
// Note: This is safe because we clear the global instanceof cache whenever
// we change the prototype of any object.
Label fast_case, slow_case;
__ mov(object_map, FieldOperand(object, HeapObject::kMapOffset));
__ CompareRoot(function, scratch, Heap::kInstanceofCacheFunctionRootIndex);
__ j(not_equal, &fast_case, Label::kNear);
__ CompareRoot(object_map, scratch, Heap::kInstanceofCacheMapRootIndex);
__ j(not_equal, &fast_case, Label::kNear);
__ LoadRoot(eax, Heap::kInstanceofCacheAnswerRootIndex);
__ ret(0);
// If {object} is a smi we can safely return false if {function} is a JS
// function, otherwise we have to miss to the runtime and throw an exception.
__ bind(&object_is_smi);
__ JumpIfSmi(function, &slow_case);
__ CmpObjectType(function, JS_FUNCTION_TYPE, function_map);
__ j(not_equal, &slow_case);
__ LoadRoot(eax, Heap::kFalseValueRootIndex);
__ ret(0);
// Fast-case: The {function} must be a valid JSFunction.
__ bind(&fast_case);
__ JumpIfSmi(function, &slow_case);
__ CmpObjectType(function, JS_FUNCTION_TYPE, function_map);
__ j(not_equal, &slow_case);
// Go to the runtime if the function is not a constructor.
__ test_b(FieldOperand(function_map, Map::kBitFieldOffset),
Immediate(1 << Map::kIsConstructor));
__ j(zero, &slow_case);
// Ensure that {function} has an instance prototype.
__ test_b(FieldOperand(function_map, Map::kBitFieldOffset),
Immediate(1 << Map::kHasNonInstancePrototype));
__ j(not_zero, &slow_case);
// Get the "prototype" (or initial map) of the {function}.
__ mov(function_prototype,
FieldOperand(function, JSFunction::kPrototypeOrInitialMapOffset));
__ AssertNotSmi(function_prototype);
// Resolve the prototype if the {function} has an initial map. Afterwards the
// {function_prototype} will be either the JSReceiver prototype object or the
// hole value, which means that no instances of the {function} were created so
// far and hence we should return false.
Label function_prototype_valid;
Register const function_prototype_map = scratch;
__ CmpObjectType(function_prototype, MAP_TYPE, function_prototype_map);
__ j(not_equal, &function_prototype_valid, Label::kNear);
__ mov(function_prototype,
FieldOperand(function_prototype, Map::kPrototypeOffset));
__ bind(&function_prototype_valid);
__ AssertNotSmi(function_prototype);
// Update the global instanceof cache with the current {object} map and
// {function}. The cached answer will be set when it is known below.
__ StoreRoot(function, scratch, Heap::kInstanceofCacheFunctionRootIndex);
__ StoreRoot(object_map, scratch, Heap::kInstanceofCacheMapRootIndex);
// Loop through the prototype chain looking for the {function} prototype.
// Assume true, and change to false if not found.
Label done, loop, fast_runtime_fallback;
__ mov(eax, isolate()->factory()->true_value());
__ bind(&loop);
// Check if the object needs to be access checked.
__ test_b(FieldOperand(object_map, Map::kBitFieldOffset),
Immediate(1 << Map::kIsAccessCheckNeeded));
__ j(not_zero, &fast_runtime_fallback, Label::kNear);
// Check if the current object is a Proxy.
__ CmpInstanceType(object_map, JS_PROXY_TYPE);
__ j(equal, &fast_runtime_fallback, Label::kNear);
__ mov(object, FieldOperand(object_map, Map::kPrototypeOffset));
__ cmp(object, function_prototype);
__ j(equal, &done, Label::kNear);
__ mov(object_map, FieldOperand(object, HeapObject::kMapOffset));
__ cmp(object, isolate()->factory()->null_value());
__ j(not_equal, &loop);
__ mov(eax, isolate()->factory()->false_value());
__ bind(&done);
__ StoreRoot(eax, scratch, Heap::kInstanceofCacheAnswerRootIndex);
__ ret(0);
// Found Proxy or access check needed: Call the runtime.
__ bind(&fast_runtime_fallback);
__ PopReturnAddressTo(scratch);
__ Push(object);
__ Push(function_prototype);
__ PushReturnAddressFrom(scratch);
// Invalidate the instanceof cache.
__ Move(eax, Immediate(Smi::FromInt(0)));
__ StoreRoot(eax, scratch, Heap::kInstanceofCacheFunctionRootIndex);
__ TailCallRuntime(Runtime::kHasInPrototypeChain);
// Slow-case: Call the %InstanceOf runtime function.
__ bind(&slow_case);
__ PopReturnAddressTo(scratch);
__ Push(object);
__ Push(function);
__ PushReturnAddressFrom(scratch);
__ TailCallRuntime(is_es6_instanceof() ? Runtime::kOrdinaryHasInstance
: Runtime::kInstanceOf);
}
// -------------------------------------------------------------------------
// StringCharCodeAtGenerator

View File

@ -51,10 +51,6 @@ const Register StoreGlobalViaContextDescriptor::SlotRegister() { return ebx; }
const Register StoreGlobalViaContextDescriptor::ValueRegister() { return eax; }
const Register InstanceOfDescriptor::LeftRegister() { return edx; }
const Register InstanceOfDescriptor::RightRegister() { return eax; }
const Register StringCompareDescriptor::LeftRegister() { return edx; }
const Register StringCompareDescriptor::RightRegister() { return eax; }

View File

@ -178,13 +178,6 @@ void StoreGlobalViaContextDescriptor::InitializePlatformSpecific(
}
void InstanceOfDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {LeftRegister(), RightRegister()};
data->InitializePlatformSpecific(arraysize(registers), registers);
}
void StringCompareDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {LeftRegister(), RightRegister()};

View File

@ -21,7 +21,6 @@ class PlatformInterfaceDescriptor;
V(VectorStoreTransition) \
V(VectorStoreICTrampoline) \
V(VectorStoreIC) \
V(InstanceOf) \
V(LoadWithVector) \
V(FastArrayPush) \
V(FastNewClosure) \
@ -336,16 +335,6 @@ class VectorStoreTransitionDescriptor : public StoreDescriptor {
};
class InstanceOfDescriptor final : public CallInterfaceDescriptor {
public:
DECLARE_DESCRIPTOR(InstanceOfDescriptor, CallInterfaceDescriptor)
enum ParameterIndices { kLeftIndex, kRightIndex, kParameterCount };
static const Register LeftRegister();
static const Register RightRegister();
};
class VectorStoreICTrampolineDescriptor : public StoreDescriptor {
public:
DECLARE_DESCRIPTOR_WITH_CUSTOM_FUNCTION_TYPE(

View File

@ -1157,7 +1157,7 @@ void Interpreter::DoTestIn(InterpreterAssembler* assembler) {
// Test if the object referenced by the <src> register is an an instance of type
// referenced by the accumulator.
void Interpreter::DoTestInstanceOf(InterpreterAssembler* assembler) {
DoBinaryOp(Runtime::kInstanceOf, assembler);
DoBinaryOp(CodeFactory::InstanceOf(isolate_), assembler);
}
void Interpreter::DoTypeConversionOp(Callable callable,

View File

@ -120,6 +120,12 @@ bool Isolate::IsArraySpeciesLookupChainIntact() {
Smi::cast(species_cell->value())->value() == kArrayProtectorValid;
}
bool Isolate::IsHasInstanceLookupChainIntact() {
if (!FLAG_harmony_instanceof) return true;
PropertyCell* has_instance_cell = heap()->has_instance_protector();
return has_instance_cell->value() == Smi::FromInt(kArrayProtectorValid);
}
} // namespace internal
} // namespace v8

View File

@ -2652,6 +2652,15 @@ void Isolate::UpdateArrayProtectorOnSetElement(Handle<JSObject> object) {
handle(Smi::FromInt(kArrayProtectorInvalid), this));
}
void Isolate::InvalidateHasInstanceProtector() {
DCHECK(factory()->has_instance_protector()->value()->IsSmi());
DCHECK(IsHasInstanceLookupChainIntact());
PropertyCell::SetValueWithInvalidation(
factory()->has_instance_protector(),
handle(Smi::FromInt(kArrayProtectorInvalid), this));
DCHECK(!IsHasInstanceLookupChainIntact());
}
void Isolate::InvalidateIsConcatSpreadableProtector() {
DCHECK(factory()->is_concat_spreadable_protector()->value()->IsSmi());
DCHECK(IsIsConcatSpreadableLookupChainIntact());

View File

@ -959,6 +959,7 @@ class Isolate {
bool IsFastArrayConstructorPrototypeChainIntact();
inline bool IsArraySpeciesLookupChainIntact();
inline bool IsHasInstanceLookupChainIntact();
bool IsIsConcatSpreadableLookupChainIntact();
// On intent to set an element in object, make sure that appropriate
@ -976,6 +977,7 @@ class Isolate {
UpdateArrayProtectorOnSetElement(object);
}
void InvalidateArraySpeciesProtector();
void InvalidateHasInstanceProtector();
void InvalidateIsConcatSpreadableProtector();
// Returns true if array is the initial array prototype in any native context.

View File

@ -191,6 +191,9 @@ void LookupIterator::InternalUpdateProtector() {
} else if (*name_ == heap()->is_concat_spreadable_symbol()) {
if (!isolate_->IsIsConcatSpreadableLookupChainIntact()) return;
isolate_->InvalidateIsConcatSpreadableProtector();
} else if (*name_ == heap()->has_instance_symbol()) {
if (!isolate_->IsHasInstanceLookupChainIntact()) return;
isolate_->InvalidateHasInstanceProtector();
}
}

View File

@ -263,7 +263,9 @@ class LookupIterator final BASE_EMBEDDED {
if (IsElement()) return;
if (*name_ == heap()->is_concat_spreadable_symbol() ||
(FLAG_harmony_species && (*name_ == heap()->constructor_string() ||
*name_ == heap()->species_symbol()))) {
*name_ == heap()->species_symbol())) ||
(FLAG_harmony_instanceof &&
(*name_ == heap()->has_instance_symbol()))) {
InternalUpdateProtector();
}
}

View File

@ -94,8 +94,6 @@ class CallSite {
T(ArrayFunctionsOnSealed, "Cannot add/remove sealed array elements") \
T(ArrayNotSubclassable, "Subclassing Arrays is not currently supported.") \
T(CalledNonCallable, "% is not a function") \
T(CalledNonCallableInstanceOf, \
"Right-hand side of 'instanceof' is not callable") \
T(CalledOnNonObject, "% called on non-object") \
T(CalledOnNullOrUndefined, "% called on null or undefined") \
T(CallSiteExpectsFunction, \
@ -132,8 +130,6 @@ class CallSite {
T(GeneratorRunning, "Generator is already running") \
T(IllegalInvocation, "Illegal invocation") \
T(IncompatibleMethodReceiver, "Method % called on incompatible receiver %") \
T(InstanceofFunctionExpected, \
"Expecting a function in instanceof check, but got %") \
T(InstanceofNonobjectProto, \
"Function has non-object prototype '%' in instanceof check") \
T(InvalidArgument, "invalid_argument") \
@ -150,6 +146,8 @@ class CallSite {
"Method invoked on undefined or null value.") \
T(MethodInvokedOnWrongType, "Method invoked on an object that is not %.") \
T(NoAccess, "no access") \
T(NonCallableInInstanceOfCheck, \
"Right-hand side of 'instanceof' is not callable") \
T(NonCoercible, "Cannot match against 'undefined' or 'null'.") \
T(NonExtensibleProto, "% is not extensible") \
T(NonObjectInInstanceOfCheck, \

View File

@ -1746,28 +1746,6 @@ void Builtins::Generate_DatePrototype_GetField(MacroAssembler* masm,
__ TailCallRuntime(Runtime::kThrowNotDateError);
}
// static
void Builtins::Generate_FunctionHasInstance(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- a0 : argc
// -- sp[0] : first argument (left-hand side)
// -- sp[4] : receiver (right-hand side)
// -----------------------------------
{
FrameScope scope(masm, StackFrame::INTERNAL);
__ lw(InstanceOfDescriptor::LeftRegister(),
MemOperand(fp, 2 * kPointerSize)); // Load left-hand side.
__ lw(InstanceOfDescriptor::RightRegister(),
MemOperand(fp, 3 * kPointerSize)); // Load right-hand side.
InstanceOfStub stub(masm->isolate(), true);
__ CallStub(&stub);
}
// Pop the argument and the receiver.
__ DropAndRet(2);
}
// static
void Builtins::Generate_FunctionPrototypeApply(MacroAssembler* masm) {
// ----------- S t a t e -------------

View File

@ -1454,128 +1454,6 @@ void LoadIndexedStringStub::Generate(MacroAssembler* masm) {
}
void InstanceOfStub::Generate(MacroAssembler* masm) {
Register const object = a1; // Object (lhs).
Register const function = a0; // Function (rhs).
Register const object_map = a2; // Map of {object}.
Register const function_map = a3; // Map of {function}.
Register const function_prototype = t0; // Prototype of {function}.
Register const scratch = t1;
DCHECK(object.is(InstanceOfDescriptor::LeftRegister()));
DCHECK(function.is(InstanceOfDescriptor::RightRegister()));
// Check if {object} is a smi.
Label object_is_smi;
__ JumpIfSmi(object, &object_is_smi);
// Lookup the {function} and the {object} map in the global instanceof cache.
// Note: This is safe because we clear the global instanceof cache whenever
// we change the prototype of any object.
Label fast_case, slow_case;
__ lw(object_map, FieldMemOperand(object, HeapObject::kMapOffset));
__ LoadRoot(at, Heap::kInstanceofCacheFunctionRootIndex);
__ Branch(&fast_case, ne, function, Operand(at));
__ LoadRoot(at, Heap::kInstanceofCacheMapRootIndex);
__ Branch(&fast_case, ne, object_map, Operand(at));
__ Ret(USE_DELAY_SLOT);
__ LoadRoot(v0, Heap::kInstanceofCacheAnswerRootIndex); // In delay slot.
// If {object} is a smi we can safely return false if {function} is a JS
// function, otherwise we have to miss to the runtime and throw an exception.
__ bind(&object_is_smi);
__ JumpIfSmi(function, &slow_case);
__ GetObjectType(function, function_map, scratch);
__ Branch(&slow_case, ne, scratch, Operand(JS_FUNCTION_TYPE));
__ Ret(USE_DELAY_SLOT);
__ LoadRoot(v0, Heap::kFalseValueRootIndex); // In delay slot.
// Fast-case: The {function} must be a valid JSFunction.
__ bind(&fast_case);
__ JumpIfSmi(function, &slow_case);
__ GetObjectType(function, function_map, scratch);
__ Branch(&slow_case, ne, scratch, Operand(JS_FUNCTION_TYPE));
// Go to the runtime if the function is not a constructor.
__ lbu(scratch, FieldMemOperand(function_map, Map::kBitFieldOffset));
__ And(at, scratch, Operand(1 << Map::kIsConstructor));
__ Branch(&slow_case, eq, at, Operand(zero_reg));
// Ensure that {function} has an instance prototype.
__ And(at, scratch, Operand(1 << Map::kHasNonInstancePrototype));
__ Branch(&slow_case, ne, at, Operand(zero_reg));
// Get the "prototype" (or initial map) of the {function}.
__ lw(function_prototype,
FieldMemOperand(function, JSFunction::kPrototypeOrInitialMapOffset));
__ AssertNotSmi(function_prototype);
// Resolve the prototype if the {function} has an initial map. Afterwards the
// {function_prototype} will be either the JSReceiver prototype object or the
// hole value, which means that no instances of the {function} were created so
// far and hence we should return false.
Label function_prototype_valid;
__ GetObjectType(function_prototype, scratch, scratch);
__ Branch(&function_prototype_valid, ne, scratch, Operand(MAP_TYPE));
__ lw(function_prototype,
FieldMemOperand(function_prototype, Map::kPrototypeOffset));
__ bind(&function_prototype_valid);
__ AssertNotSmi(function_prototype);
// Update the global instanceof cache with the current {object} map and
// {function}. The cached answer will be set when it is known below.
__ StoreRoot(function, Heap::kInstanceofCacheFunctionRootIndex);
__ StoreRoot(object_map, Heap::kInstanceofCacheMapRootIndex);
// Loop through the prototype chain looking for the {function} prototype.
// Assume true, and change to false if not found.
Register const object_instance_type = function_map;
Register const map_bit_field = function_map;
Register const null = scratch;
Register const result = v0;
Label done, loop, fast_runtime_fallback;
__ LoadRoot(result, Heap::kTrueValueRootIndex);
__ LoadRoot(null, Heap::kNullValueRootIndex);
__ bind(&loop);
// Check if the object needs to be access checked.
__ lbu(map_bit_field, FieldMemOperand(object_map, Map::kBitFieldOffset));
__ And(map_bit_field, map_bit_field, Operand(1 << Map::kIsAccessCheckNeeded));
__ Branch(&fast_runtime_fallback, ne, map_bit_field, Operand(zero_reg));
// Check if the current object is a Proxy.
__ lbu(object_instance_type,
FieldMemOperand(object_map, Map::kInstanceTypeOffset));
__ Branch(&fast_runtime_fallback, eq, object_instance_type,
Operand(JS_PROXY_TYPE));
__ lw(object, FieldMemOperand(object_map, Map::kPrototypeOffset));
__ Branch(&done, eq, object, Operand(function_prototype));
__ Branch(USE_DELAY_SLOT, &loop, ne, object, Operand(null));
__ lw(object_map,
FieldMemOperand(object, HeapObject::kMapOffset)); // In delay slot.
__ LoadRoot(result, Heap::kFalseValueRootIndex);
__ bind(&done);
__ Ret(USE_DELAY_SLOT);
__ StoreRoot(result,
Heap::kInstanceofCacheAnswerRootIndex); // In delay slot.
// Found Proxy or access check needed: Call the runtime
__ bind(&fast_runtime_fallback);
__ Push(object, function_prototype);
// Invalidate the instanceof cache.
DCHECK(Smi::FromInt(0) == 0);
__ StoreRoot(zero_reg, Heap::kInstanceofCacheFunctionRootIndex);
__ TailCallRuntime(Runtime::kHasInPrototypeChain);
// Slow-case: Call the %InstanceOf runtime function.
__ bind(&slow_case);
__ Push(object, function);
__ TailCallRuntime(is_es6_instanceof() ? Runtime::kOrdinaryHasInstance
: Runtime::kInstanceOf);
}
void FunctionPrototypeStub::Generate(MacroAssembler* masm) {
Label miss;
Register receiver = LoadDescriptor::ReceiverRegister();

View File

@ -46,10 +46,6 @@ const Register StoreGlobalViaContextDescriptor::SlotRegister() { return a2; }
const Register StoreGlobalViaContextDescriptor::ValueRegister() { return a0; }
const Register InstanceOfDescriptor::LeftRegister() { return a1; }
const Register InstanceOfDescriptor::RightRegister() { return a0; }
const Register StringCompareDescriptor::LeftRegister() { return a1; }
const Register StringCompareDescriptor::RightRegister() { return a0; }

View File

@ -1735,28 +1735,6 @@ void Builtins::Generate_DatePrototype_GetField(MacroAssembler* masm,
__ TailCallRuntime(Runtime::kThrowNotDateError);
}
// static
void Builtins::Generate_FunctionHasInstance(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- a0 : argc
// -- sp[0] : first argument (left-hand side)
// -- sp[8] : receiver (right-hand side)
// -----------------------------------
{
FrameScope scope(masm, StackFrame::INTERNAL);
__ ld(InstanceOfDescriptor::LeftRegister(),
MemOperand(fp, 2 * kPointerSize)); // Load left-hand side.
__ ld(InstanceOfDescriptor::RightRegister(),
MemOperand(fp, 3 * kPointerSize)); // Load right-hand side.
InstanceOfStub stub(masm->isolate(), true);
__ CallStub(&stub);
}
// Pop the argument and the receiver.
__ DropAndRet(2);
}
// static
void Builtins::Generate_FunctionPrototypeApply(MacroAssembler* masm) {
// ----------- S t a t e -------------

View File

@ -1450,128 +1450,6 @@ void LoadIndexedStringStub::Generate(MacroAssembler* masm) {
}
void InstanceOfStub::Generate(MacroAssembler* masm) {
Register const object = a1; // Object (lhs).
Register const function = a0; // Function (rhs).
Register const object_map = a2; // Map of {object}.
Register const function_map = a3; // Map of {function}.
Register const function_prototype = a4; // Prototype of {function}.
Register const scratch = a5;
DCHECK(object.is(InstanceOfDescriptor::LeftRegister()));
DCHECK(function.is(InstanceOfDescriptor::RightRegister()));
// Check if {object} is a smi.
Label object_is_smi;
__ JumpIfSmi(object, &object_is_smi);
// Lookup the {function} and the {object} map in the global instanceof cache.
// Note: This is safe because we clear the global instanceof cache whenever
// we change the prototype of any object.
Label fast_case, slow_case;
__ ld(object_map, FieldMemOperand(object, HeapObject::kMapOffset));
__ LoadRoot(at, Heap::kInstanceofCacheFunctionRootIndex);
__ Branch(&fast_case, ne, function, Operand(at));
__ LoadRoot(at, Heap::kInstanceofCacheMapRootIndex);
__ Branch(&fast_case, ne, object_map, Operand(at));
__ Ret(USE_DELAY_SLOT);
__ LoadRoot(v0, Heap::kInstanceofCacheAnswerRootIndex); // In delay slot.
// If {object} is a smi we can safely return false if {function} is a JS
// function, otherwise we have to miss to the runtime and throw an exception.
__ bind(&object_is_smi);
__ JumpIfSmi(function, &slow_case);
__ GetObjectType(function, function_map, scratch);
__ Branch(&slow_case, ne, scratch, Operand(JS_FUNCTION_TYPE));
__ Ret(USE_DELAY_SLOT);
__ LoadRoot(v0, Heap::kFalseValueRootIndex); // In delay slot.
// Fast-case: The {function} must be a valid JSFunction.
__ bind(&fast_case);
__ JumpIfSmi(function, &slow_case);
__ GetObjectType(function, function_map, scratch);
__ Branch(&slow_case, ne, scratch, Operand(JS_FUNCTION_TYPE));
// Go to the runtime if the function is not a constructor.
__ lbu(scratch, FieldMemOperand(function_map, Map::kBitFieldOffset));
__ And(at, scratch, Operand(1 << Map::kIsConstructor));
__ Branch(&slow_case, eq, at, Operand(zero_reg));
// Ensure that {function} has an instance prototype.
__ And(at, scratch, Operand(1 << Map::kHasNonInstancePrototype));
__ Branch(&slow_case, ne, at, Operand(zero_reg));
// Get the "prototype" (or initial map) of the {function}.
__ ld(function_prototype,
FieldMemOperand(function, JSFunction::kPrototypeOrInitialMapOffset));
__ AssertNotSmi(function_prototype);
// Resolve the prototype if the {function} has an initial map. Afterwards the
// {function_prototype} will be either the JSReceiver prototype object or the
// hole value, which means that no instances of the {function} were created so
// far and hence we should return false.
Label function_prototype_valid;
__ GetObjectType(function_prototype, scratch, scratch);
__ Branch(&function_prototype_valid, ne, scratch, Operand(MAP_TYPE));
__ ld(function_prototype,
FieldMemOperand(function_prototype, Map::kPrototypeOffset));
__ bind(&function_prototype_valid);
__ AssertNotSmi(function_prototype);
// Update the global instanceof cache with the current {object} map and
// {function}. The cached answer will be set when it is known below.
__ StoreRoot(function, Heap::kInstanceofCacheFunctionRootIndex);
__ StoreRoot(object_map, Heap::kInstanceofCacheMapRootIndex);
// Loop through the prototype chain looking for the {function} prototype.
// Assume true, and change to false if not found.
Register const object_instance_type = function_map;
Register const map_bit_field = function_map;
Register const null = scratch;
Register const result = v0;
Label done, loop, fast_runtime_fallback;
__ LoadRoot(result, Heap::kTrueValueRootIndex);
__ LoadRoot(null, Heap::kNullValueRootIndex);
__ bind(&loop);
// Check if the object needs to be access checked.
__ lbu(map_bit_field, FieldMemOperand(object_map, Map::kBitFieldOffset));
__ And(map_bit_field, map_bit_field, Operand(1 << Map::kIsAccessCheckNeeded));
__ Branch(&fast_runtime_fallback, ne, map_bit_field, Operand(zero_reg));
// Check if the current object is a Proxy.
__ lbu(object_instance_type,
FieldMemOperand(object_map, Map::kInstanceTypeOffset));
__ Branch(&fast_runtime_fallback, eq, object_instance_type,
Operand(JS_PROXY_TYPE));
__ ld(object, FieldMemOperand(object_map, Map::kPrototypeOffset));
__ Branch(&done, eq, object, Operand(function_prototype));
__ Branch(USE_DELAY_SLOT, &loop, ne, object, Operand(null));
__ ld(object_map,
FieldMemOperand(object, HeapObject::kMapOffset)); // In delay slot.
__ LoadRoot(result, Heap::kFalseValueRootIndex);
__ bind(&done);
__ Ret(USE_DELAY_SLOT);
__ StoreRoot(result,
Heap::kInstanceofCacheAnswerRootIndex); // In delay slot.
// Found Proxy or access check needed: Call the runtime
__ bind(&fast_runtime_fallback);
__ Push(object, function_prototype);
// Invalidate the instanceof cache.
DCHECK(Smi::FromInt(0) == 0);
__ StoreRoot(zero_reg, Heap::kInstanceofCacheFunctionRootIndex);
__ TailCallRuntime(Runtime::kHasInPrototypeChain);
// Slow-case: Call the %InstanceOf runtime function.
__ bind(&slow_case);
__ Push(object, function);
__ TailCallRuntime(is_es6_instanceof() ? Runtime::kOrdinaryHasInstance
: Runtime::kInstanceOf);
}
void FunctionPrototypeStub::Generate(MacroAssembler* masm) {
Label miss;
Register receiver = LoadDescriptor::ReceiverRegister();

View File

@ -46,10 +46,6 @@ const Register StoreGlobalViaContextDescriptor::SlotRegister() { return a2; }
const Register StoreGlobalViaContextDescriptor::ValueRegister() { return a0; }
const Register InstanceOfDescriptor::LeftRegister() { return a1; }
const Register InstanceOfDescriptor::RightRegister() { return a0; }
const Register StringCompareDescriptor::LeftRegister() { return a1; }
const Register StringCompareDescriptor::RightRegister() { return a0; }

View File

@ -571,6 +571,88 @@ MaybeHandle<Object> Object::BitwiseXor(Isolate* isolate, Handle<Object> lhs,
NumberToInt32(*rhs));
}
// static
MaybeHandle<Object> Object::OrdinaryHasInstance(Isolate* isolate,
Handle<Object> callable,
Handle<Object> object) {
// The {callable} must have a [[Call]] internal method.
if (!callable->IsCallable()) return isolate->factory()->false_value();
// Check if {callable} is a bound function, and if so retrieve its
// [[BoundTargetFunction]] and use that instead of {callable}.
if (callable->IsJSBoundFunction()) {
Handle<Object> bound_callable(
Handle<JSBoundFunction>::cast(callable)->bound_target_function(),
isolate);
return Object::InstanceOf(isolate, object, bound_callable);
}
// If {object} is not a receiver, return false.
if (!object->IsJSReceiver()) return isolate->factory()->false_value();
// Get the "prototype" of {callable}; raise an error if it's not a receiver.
Handle<Object> prototype;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, prototype,
Object::GetProperty(callable, isolate->factory()->prototype_string()),
Object);
if (!prototype->IsJSReceiver()) {
THROW_NEW_ERROR(
isolate,
NewTypeError(MessageTemplate::kInstanceofNonobjectProto, prototype),
Object);
}
// Return whether or not {prototype} is in the prototype chain of {object}.
Maybe<bool> result = JSReceiver::HasInPrototypeChain(
isolate, Handle<JSReceiver>::cast(object), prototype);
if (result.IsNothing()) return MaybeHandle<Object>();
return isolate->factory()->ToBoolean(result.FromJust());
}
// static
MaybeHandle<Object> Object::InstanceOf(Isolate* isolate, Handle<Object> object,
Handle<Object> callable) {
if (FLAG_harmony_instanceof) {
// The {callable} must be a receiver.
if (!callable->IsJSReceiver()) {
THROW_NEW_ERROR(
isolate, NewTypeError(MessageTemplate::kNonObjectInInstanceOfCheck),
Object);
}
// Lookup the @@hasInstance method on {callable}.
Handle<Object> inst_of_handler;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, inst_of_handler,
JSReceiver::GetMethod(Handle<JSReceiver>::cast(callable),
isolate->factory()->has_instance_symbol()),
Object);
if (!inst_of_handler->IsUndefined()) {
// Call the {inst_of_handler} on the {callable}.
Handle<Object> result;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, result,
Execution::Call(isolate, inst_of_handler, callable, 1, &object),
Object);
return isolate->factory()->ToBoolean(result->BooleanValue());
}
}
// The {callable} must have a [[Call]] internal method.
if (!callable->IsCallable()) {
THROW_NEW_ERROR(
isolate, NewTypeError(MessageTemplate::kNonCallableInInstanceOfCheck),
Object);
}
// Fall back to OrdinaryHasInstance with {callable} and {object}.
Handle<Object> result;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, result,
JSReceiver::OrdinaryHasInstance(isolate, callable, object), Object);
return result;
}
Maybe<bool> Object::IsArray(Handle<Object> object) {
if (object->IsJSArray()) return Just(true);
@ -1389,6 +1471,7 @@ void JSObject::SetNormalizedProperty(Handle<JSObject> object,
}
}
// static
Maybe<bool> JSReceiver::HasInPrototypeChain(Isolate* isolate,
Handle<JSReceiver> object,
Handle<Object> proto) {
@ -1402,7 +1485,6 @@ Maybe<bool> JSReceiver::HasInPrototypeChain(Isolate* isolate,
}
}
Map* Object::GetRootMap(Isolate* isolate) {
DisallowHeapAllocation no_alloc;
if (IsSmi()) {

View File

@ -1233,6 +1233,14 @@ class Object {
Handle<Object> lhs,
Handle<Object> rhs);
// ES6 section 7.3.19 OrdinaryHasInstance (C, O).
MUST_USE_RESULT static MaybeHandle<Object> OrdinaryHasInstance(
Isolate* isolate, Handle<Object> callable, Handle<Object> object);
// ES6 section 12.10.4 Runtime Semantics: InstanceofOperator(O, C)
MUST_USE_RESULT static MaybeHandle<Object> InstanceOf(
Isolate* isolate, Handle<Object> object, Handle<Object> callable);
MUST_USE_RESULT static MaybeHandle<Object> GetProperty(LookupIterator* it);
// ES6 [[Set]] (when passed DONT_THROW)

View File

@ -2561,16 +2561,11 @@ ParserBase<Traits>::ParseBinaryExpression(int prec, bool accept_IN,
case Token::NE_STRICT: cmp = Token::EQ_STRICT; break;
default: break;
}
if (FLAG_harmony_instanceof && cmp == Token::INSTANCEOF) {
x = Traits::RewriteInstanceof(x, y, pos);
} else {
x = factory()->NewCompareOperation(cmp, x, y, pos);
if (cmp != op) {
// The comparison was negated - add a NOT.
x = factory()->NewUnaryOperation(Token::NOT, x, pos);
}
x = factory()->NewCompareOperation(cmp, x, y, pos);
if (cmp != op) {
// The comparison was negated - add a NOT.
x = factory()->NewUnaryOperation(Token::NOT, x, pos);
}
} else if (op == Token::EXP) {
x = Traits::RewriteExponentiation(x, y, pos);
} else {

View File

@ -6366,180 +6366,6 @@ Expression* ParserTraits::RewriteYieldStar(
return yield_star;
}
// Desugaring of (lhs) instanceof (rhs)
// ====================================
//
// We desugar instanceof into a load of property @@hasInstance on the rhs.
// We end up with roughly the following code (O, C):
//
// do {
// let O = lhs;
// let C = rhs;
// if (!IS_RECEIVER(C)) throw MakeTypeError(kNonObjectInInstanceOfCheck);
// let handler_result = C[Symbol.hasInstance];
// if (handler_result === undefined) {
// if (!IS_CALLABLE(C)) {
// throw MakeTypeError(kCalledNonCallableInstanceOf);
// }
// handler_result = %_GetOrdinaryHasInstance()
// handler_result = %_Call(handler_result, C, O);
// } else {
// handler_result = !!(%_Call(handler_result, C, O));
// }
// handler_result;
// }
//
Expression* ParserTraits::RewriteInstanceof(Expression* lhs, Expression* rhs,
int pos) {
const int nopos = RelocInfo::kNoPosition;
auto factory = parser_->factory();
auto avfactory = parser_->ast_value_factory();
auto scope = parser_->scope_;
auto zone = parser_->zone();
// let O = lhs;
Variable* var_O = scope->NewTemporary(avfactory->empty_string());
Statement* get_O;
{
Expression* O_proxy = factory->NewVariableProxy(var_O);
Expression* assignment =
factory->NewAssignment(Token::ASSIGN, O_proxy, lhs, nopos);
get_O = factory->NewExpressionStatement(assignment, nopos);
}
// let C = lhs;
Variable* var_C = scope->NewTemporary(avfactory->empty_string());
Statement* get_C;
{
Expression* C_proxy = factory->NewVariableProxy(var_C);
Expression* assignment =
factory->NewAssignment(Token::ASSIGN, C_proxy, rhs, nopos);
get_C = factory->NewExpressionStatement(assignment, nopos);
}
// if (!IS_RECEIVER(C)) throw MakeTypeError(kNonObjectInInstanceOfCheck);
Statement* validate_C;
{
auto args = new (zone) ZoneList<Expression*>(1, zone);
args->Add(factory->NewVariableProxy(var_C), zone);
Expression* is_receiver_call =
factory->NewCallRuntime(Runtime::kInlineIsJSReceiver, args, nopos);
Expression* call =
NewThrowTypeError(MessageTemplate::kNonObjectInInstanceOfCheck,
avfactory->empty_string(), pos);
Statement* throw_call = factory->NewExpressionStatement(call, pos);
validate_C =
factory->NewIfStatement(is_receiver_call,
factory->NewEmptyStatement(nopos),
throw_call,
nopos);
}
// let handler_result = C[Symbol.hasInstance];
Variable* var_handler_result = scope->NewTemporary(avfactory->empty_string());
Statement* initialize_handler;
{
Expression* hasInstance_symbol_literal =
factory->NewSymbolLiteral("hasInstance_symbol", RelocInfo::kNoPosition);
Expression* prop = factory->NewProperty(factory->NewVariableProxy(var_C),
hasInstance_symbol_literal, pos);
Expression* handler_proxy = factory->NewVariableProxy(var_handler_result);
Expression* assignment =
factory->NewAssignment(Token::ASSIGN, handler_proxy, prop, nopos);
initialize_handler = factory->NewExpressionStatement(assignment, nopos);
}
// if (handler_result === undefined) {
// if (!IS_CALLABLE(C)) {
// throw MakeTypeError(kCalledNonCallableInstanceOf);
// }
// handler_result = %_GetOrdinaryHasInstance()
// handler_result = %_Call(handler_result, C, O);
// } else {
// handler_result = !!%_Call(handler_result, C, O);
// }
Statement* call_handler;
{
Expression* condition = factory->NewCompareOperation(
Token::EQ_STRICT, factory->NewVariableProxy(var_handler_result),
factory->NewUndefinedLiteral(nopos), nopos);
Block* then_side = factory->NewBlock(nullptr, 3, false, nopos);
{
Expression* throw_expr =
NewThrowTypeError(MessageTemplate::kCalledNonCallableInstanceOf,
avfactory->empty_string(), pos);
Statement* validate_C = CheckCallable(var_C, throw_expr, pos);
ZoneList<Expression*>* empty_args =
new (zone) ZoneList<Expression*>(0, zone);
Expression* ordinary_has_instance = factory->NewCallRuntime(
Runtime::kInlineGetOrdinaryHasInstance, empty_args, pos);
Expression* handler_proxy = factory->NewVariableProxy(var_handler_result);
Expression* assignment_handler = factory->NewAssignment(
Token::ASSIGN, handler_proxy, ordinary_has_instance, nopos);
Statement* assignment_get_handler =
factory->NewExpressionStatement(assignment_handler, nopos);
ZoneList<Expression*>* args = new (zone) ZoneList<Expression*>(3, zone);
args->Add(factory->NewVariableProxy(var_handler_result), zone);
args->Add(factory->NewVariableProxy(var_C), zone);
args->Add(factory->NewVariableProxy(var_O), zone);
Expression* call =
factory->NewCallRuntime(Runtime::kInlineCall, args, pos);
Expression* result_proxy = factory->NewVariableProxy(var_handler_result);
Expression* assignment =
factory->NewAssignment(Token::ASSIGN, result_proxy, call, nopos);
Statement* assignment_return =
factory->NewExpressionStatement(assignment, nopos);
then_side->statements()->Add(validate_C, zone);
then_side->statements()->Add(assignment_get_handler, zone);
then_side->statements()->Add(assignment_return, zone);
}
Statement* else_side;
{
auto args = new (zone) ZoneList<Expression*>(3, zone);
args->Add(factory->NewVariableProxy(var_handler_result), zone);
args->Add(factory->NewVariableProxy(var_C), zone);
args->Add(factory->NewVariableProxy(var_O), zone);
Expression* call =
factory->NewCallRuntime(Runtime::kInlineCall, args, nopos);
Expression* inner_not =
factory->NewUnaryOperation(Token::NOT, call, nopos);
Expression* outer_not =
factory->NewUnaryOperation(Token::NOT, inner_not, nopos);
Expression* result_proxy = factory->NewVariableProxy(var_handler_result);
Expression* assignment =
factory->NewAssignment(Token::ASSIGN, result_proxy, outer_not, nopos);
else_side = factory->NewExpressionStatement(assignment, nopos);
}
call_handler =
factory->NewIfStatement(condition, then_side, else_side, nopos);
}
// do { ... }
DoExpression* instanceof;
{
Block* block = factory->NewBlock(nullptr, 5, true, nopos);
block->statements()->Add(get_O, zone);
block->statements()->Add(get_C, zone);
block->statements()->Add(validate_C, zone);
block->statements()->Add(initialize_handler, zone);
block->statements()->Add(call_handler, zone);
// Here is the desugared instanceof.
instanceof = factory->NewDoExpression(block, var_handler_result, nopos);
Rewriter::Rewrite(parser_, instanceof, avfactory);
}
return instanceof;
}
Statement* ParserTraits::CheckCallable(Variable* var, Expression* error,
int pos) {
auto factory = parser_->factory();

View File

@ -677,8 +677,6 @@ class ParserTraits {
Expression* RewriteYieldStar(
Expression* generator, Expression* expression, int pos);
Expression* RewriteInstanceof(Expression* lhs, Expression* rhs, int pos);
private:
Parser* parser_;

View File

@ -992,9 +992,6 @@ class PreParserTraits {
inline PreParserExpression RewriteYieldStar(
PreParserExpression generator, PreParserExpression expr, int pos);
inline PreParserExpression RewriteInstanceof(PreParserExpression lhs,
PreParserExpression rhs,
int pos);
private:
PreParser* pre_parser_;
@ -1232,12 +1229,6 @@ PreParserExpression PreParserTraits::RewriteYieldStar(
return PreParserExpression::Default();
}
PreParserExpression PreParserTraits::RewriteInstanceof(PreParserExpression lhs,
PreParserExpression rhs,
int pos) {
return PreParserExpression::Default();
}
PreParserStatementList PreParser::ParseEagerFunctionBody(
PreParserIdentifier function_name, int pos,
const PreParserFormalParameters& parameters, FunctionKind kind,

View File

@ -531,13 +531,6 @@ RUNTIME_FUNCTION(Runtime_IncrementUseCounter) {
return isolate->heap()->undefined_value();
}
RUNTIME_FUNCTION(Runtime_GetOrdinaryHasInstance) {
HandleScope scope(isolate);
DCHECK_EQ(0, args.length());
return isolate->native_context()->ordinary_has_instance();
}
RUNTIME_FUNCTION(Runtime_GetAndResetRuntimeCallStats) {
HandleScope scope(isolate);
if (args.length() == 0) {
@ -598,5 +591,17 @@ RUNTIME_FUNCTION(Runtime_RunMicrotasks) {
isolate->RunMicrotasks();
return isolate->heap()->undefined_value();
}
RUNTIME_FUNCTION(Runtime_OrdinaryHasInstance) {
HandleScope scope(isolate);
DCHECK_EQ(2, args.length());
CONVERT_ARG_HANDLE_CHECKED(Object, callable, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, object, 1);
Handle<Object> result;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, result, Object::OrdinaryHasInstance(isolate, callable, object));
return *result;
}
} // namespace internal
} // namespace v8

View File

@ -983,97 +983,6 @@ RUNTIME_FUNCTION(Runtime_Compare) {
return isolate->heap()->exception();
}
RUNTIME_FUNCTION(Runtime_InstanceOf) {
// TODO(4447): Remove this function when ES6 instanceof ships for good.
DCHECK(!FLAG_harmony_instanceof);
// ECMA-262, section 11.8.6, page 54.
HandleScope shs(isolate);
DCHECK_EQ(2, args.length());
DCHECK(args.length() == 2);
CONVERT_ARG_HANDLE_CHECKED(Object, object, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, callable, 1);
// {callable} must have a [[Call]] internal method.
if (!callable->IsCallable()) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate,
NewTypeError(MessageTemplate::kInstanceofFunctionExpected, callable));
}
// If {object} is not a receiver, return false.
if (!object->IsJSReceiver()) {
return isolate->heap()->false_value();
}
// Check if {callable} is bound, if so, get [[BoundTargetFunction]] from it
// and use that instead of {callable}.
while (callable->IsJSBoundFunction()) {
callable =
handle(Handle<JSBoundFunction>::cast(callable)->bound_target_function(),
isolate);
}
DCHECK(callable->IsCallable());
// Get the "prototype" of {callable}; raise an error if it's not a receiver.
Handle<Object> prototype;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, prototype,
JSReceiver::GetProperty(Handle<JSReceiver>::cast(callable),
isolate->factory()->prototype_string()));
if (!prototype->IsJSReceiver()) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate,
NewTypeError(MessageTemplate::kInstanceofNonobjectProto, prototype));
}
// Return whether or not {prototype} is in the prototype chain of {object}.
Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(object);
Maybe<bool> result =
JSReceiver::HasInPrototypeChain(isolate, receiver, prototype);
MAYBE_RETURN(result, isolate->heap()->exception());
return isolate->heap()->ToBoolean(result.FromJust());
}
RUNTIME_FUNCTION(Runtime_OrdinaryHasInstance) {
// ES6 section 19.2.3.6 Function.prototype[@@hasInstance](V)
HandleScope shs(isolate);
DCHECK_EQ(2, args.length());
DCHECK(args.length() == 2);
CONVERT_ARG_HANDLE_CHECKED(Object, object, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, callable, 1);
// {callable} must have a [[Call]] internal method.
if (!callable->IsCallable()) {
return isolate->heap()->false_value();
}
// If {object} is not a receiver, return false.
if (!object->IsJSReceiver()) {
return isolate->heap()->false_value();
}
// Check if {callable} is bound, if so, get [[BoundTargetFunction]] from it
// and use that instead of {callable}.
while (callable->IsJSBoundFunction()) {
callable =
handle(Handle<JSBoundFunction>::cast(callable)->bound_target_function(),
isolate);
}
DCHECK(callable->IsCallable());
// Get the "prototype" of {callable}; raise an error if it's not a receiver.
Handle<Object> prototype;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, prototype,
JSReceiver::GetProperty(Handle<JSReceiver>::cast(callable),
isolate->factory()->prototype_string()));
if (!prototype->IsJSReceiver()) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate,
NewTypeError(MessageTemplate::kInstanceofNonobjectProto, prototype));
}
// Return whether or not {prototype} is in the prototype chain of {object}.
Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(object);
Maybe<bool> result =
JSReceiver::HasInPrototypeChain(isolate, receiver, prototype);
MAYBE_RETURN(result, isolate->heap()->exception());
return isolate->heap()->ToBoolean(result.FromJust());
}
RUNTIME_FUNCTION(Runtime_HasInPrototypeChain) {
HandleScope scope(isolate);
DCHECK_EQ(2, args.length());

View File

@ -216,5 +216,16 @@ RUNTIME_FUNCTION(Runtime_GreaterThanOrEqual) {
return isolate->heap()->ToBoolean(result.FromJust());
}
RUNTIME_FUNCTION(Runtime_InstanceOf) {
HandleScope shs(isolate);
DCHECK_EQ(2, args.length());
CONVERT_ARG_HANDLE_CHECKED(Object, object, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, callable, 1);
Handle<Object> result;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, result, Object::InstanceOf(isolate, object, callable));
return *result;
}
} // namespace internal
} // namespace v8

View File

@ -317,11 +317,11 @@ namespace internal {
F(ThrowCalledOnNullOrUndefined, 1, 1) \
F(CreateListFromArrayLike, 1, 1) \
F(IncrementUseCounter, 1, 1) \
F(GetOrdinaryHasInstance, 0, 1) \
F(GetAndResetRuntimeCallStats, -1 /* <= 2 */, 1) \
F(EnqueueMicrotask, 1, 1) \
F(RunMicrotasks, 0, 1) \
F(WasmGetFunctionName, 2, 1)
F(WasmGetFunctionName, 2, 1) \
F(OrdinaryHasInstance, 2, 1)
#define FOR_EACH_INTRINSIC_JSON(F) \
F(QuoteJSONString, 1, 1) \
@ -432,8 +432,6 @@ namespace internal {
F(SameValue, 2, 1) \
F(SameValueZero, 2, 1) \
F(Compare, 3, 1) \
F(InstanceOf, 2, 1) \
F(OrdinaryHasInstance, 2, 1) \
F(HasInPrototypeChain, 2, 1) \
F(CreateIterResultObject, 2, 1) \
F(IsAccessCheckNeeded, 1, 1) \
@ -458,7 +456,8 @@ namespace internal {
F(LessThan, 2, 1) \
F(GreaterThan, 2, 1) \
F(LessThanOrEqual, 2, 1) \
F(GreaterThanOrEqual, 2, 1)
F(GreaterThanOrEqual, 2, 1) \
F(InstanceOf, 2, 1)
#define FOR_EACH_INTRINSIC_PROXY(F) \
F(IsJSProxy, 1, 1) \

View File

@ -1261,29 +1261,6 @@ void Builtins::Generate_DatePrototype_GetField(MacroAssembler* masm,
}
}
// static
void Builtins::Generate_FunctionHasInstance(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- rax : argc
// -- rsp[0] : return address
// -- rsp[8] : first argument (left-hand side)
// -- rsp[16] : receiver (right-hand side)
// -----------------------------------
{
FrameScope scope(masm, StackFrame::INTERNAL);
__ movp(InstanceOfDescriptor::LeftRegister(),
Operand(rbp, 2 * kPointerSize)); // Load left-hand side.
__ movp(InstanceOfDescriptor::RightRegister(),
Operand(rbp, 3 * kPointerSize)); // Load right-hand side.
InstanceOfStub stub(masm->isolate(), true);
__ CallStub(&stub);
}
// Pop the argument and the receiver.
__ ret(2 * kPointerSize);
}
// static
void Builtins::Generate_FunctionPrototypeApply(MacroAssembler* masm) {
// ----------- S t a t e -------------

View File

@ -2002,125 +2002,6 @@ void JSEntryStub::Generate(MacroAssembler* masm) {
}
void InstanceOfStub::Generate(MacroAssembler* masm) {
Register const object = rdx; // Object (lhs).
Register const function = rax; // Function (rhs).
Register const object_map = rcx; // Map of {object}.
Register const function_map = r8; // Map of {function}.
Register const function_prototype = rdi; // Prototype of {function}.
DCHECK(object.is(InstanceOfDescriptor::LeftRegister()));
DCHECK(function.is(InstanceOfDescriptor::RightRegister()));
// Check if {object} is a smi.
Label object_is_smi;
__ JumpIfSmi(object, &object_is_smi, Label::kNear);
// Lookup the {function} and the {object} map in the global instanceof cache.
// Note: This is safe because we clear the global instanceof cache whenever
// we change the prototype of any object.
Label fast_case, slow_case;
__ movp(object_map, FieldOperand(object, HeapObject::kMapOffset));
__ CompareRoot(function, Heap::kInstanceofCacheFunctionRootIndex);
__ j(not_equal, &fast_case, Label::kNear);
__ CompareRoot(object_map, Heap::kInstanceofCacheMapRootIndex);
__ j(not_equal, &fast_case, Label::kNear);
__ LoadRoot(rax, Heap::kInstanceofCacheAnswerRootIndex);
__ ret(0);
// If {object} is a smi we can safely return false if {function} is a JS
// function, otherwise we have to miss to the runtime and throw an exception.
__ bind(&object_is_smi);
__ JumpIfSmi(function, &slow_case);
__ CmpObjectType(function, JS_FUNCTION_TYPE, function_map);
__ j(not_equal, &slow_case);
__ LoadRoot(rax, Heap::kFalseValueRootIndex);
__ ret(0);
// Fast-case: The {function} must be a valid JSFunction.
__ bind(&fast_case);
__ JumpIfSmi(function, &slow_case);
__ CmpObjectType(function, JS_FUNCTION_TYPE, function_map);
__ j(not_equal, &slow_case);
// Go to the runtime if the function is not a constructor.
__ testb(FieldOperand(function_map, Map::kBitFieldOffset),
Immediate(1 << Map::kIsConstructor));
__ j(zero, &slow_case);
// Ensure that {function} has an instance prototype.
__ testb(FieldOperand(function_map, Map::kBitFieldOffset),
Immediate(1 << Map::kHasNonInstancePrototype));
__ j(not_zero, &slow_case);
// Get the "prototype" (or initial map) of the {function}.
__ movp(function_prototype,
FieldOperand(function, JSFunction::kPrototypeOrInitialMapOffset));
__ AssertNotSmi(function_prototype);
// Resolve the prototype if the {function} has an initial map. Afterwards the
// {function_prototype} will be either the JSReceiver prototype object or the
// hole value, which means that no instances of the {function} were created so
// far and hence we should return false.
Label function_prototype_valid;
Register const function_prototype_map = kScratchRegister;
__ CmpObjectType(function_prototype, MAP_TYPE, function_prototype_map);
__ j(not_equal, &function_prototype_valid, Label::kNear);
__ movp(function_prototype,
FieldOperand(function_prototype, Map::kPrototypeOffset));
__ bind(&function_prototype_valid);
__ AssertNotSmi(function_prototype);
// Update the global instanceof cache with the current {object} map and
// {function}. The cached answer will be set when it is known below.
__ StoreRoot(function, Heap::kInstanceofCacheFunctionRootIndex);
__ StoreRoot(object_map, Heap::kInstanceofCacheMapRootIndex);
// Loop through the prototype chain looking for the {function} prototype.
// Assume true, and change to false if not found.
Label done, loop, fast_runtime_fallback;
__ LoadRoot(rax, Heap::kTrueValueRootIndex);
__ bind(&loop);
__ testb(FieldOperand(object_map, Map::kBitFieldOffset),
Immediate(1 << Map::kIsAccessCheckNeeded));
__ j(not_zero, &fast_runtime_fallback, Label::kNear);
__ CmpInstanceType(object_map, JS_PROXY_TYPE);
__ j(equal, &fast_runtime_fallback, Label::kNear);
__ movp(object, FieldOperand(object_map, Map::kPrototypeOffset));
__ cmpp(object, function_prototype);
__ j(equal, &done, Label::kNear);
__ CompareRoot(object, Heap::kNullValueRootIndex);
__ movp(object_map, FieldOperand(object, HeapObject::kMapOffset));
__ j(not_equal, &loop);
__ LoadRoot(rax, Heap::kFalseValueRootIndex);
__ bind(&done);
__ StoreRoot(rax, Heap::kInstanceofCacheAnswerRootIndex);
__ ret(0);
// Found Proxy or access check needed: Call the runtime.
__ bind(&fast_runtime_fallback);
__ PopReturnAddressTo(kScratchRegister);
__ Push(object);
__ Push(function_prototype);
__ PushReturnAddressFrom(kScratchRegister);
// Invalidate the instanceof cache.
__ Move(rax, Smi::FromInt(0));
__ StoreRoot(rax, Heap::kInstanceofCacheFunctionRootIndex);
__ TailCallRuntime(Runtime::kHasInPrototypeChain);
// Slow-case: Call the %InstanceOf runtime function.
__ bind(&slow_case);
__ PopReturnAddressTo(kScratchRegister);
__ Push(object);
__ Push(function);
__ PushReturnAddressFrom(kScratchRegister);
__ TailCallRuntime(is_es6_instanceof() ? Runtime::kOrdinaryHasInstance
: Runtime::kInstanceOf);
}
// -------------------------------------------------------------------------
// StringCharCodeAtGenerator

View File

@ -46,10 +46,6 @@ const Register StoreGlobalViaContextDescriptor::SlotRegister() { return rbx; }
const Register StoreGlobalViaContextDescriptor::ValueRegister() { return rax; }
const Register InstanceOfDescriptor::LeftRegister() { return rdx; }
const Register InstanceOfDescriptor::RightRegister() { return rax; }
const Register StringCompareDescriptor::LeftRegister() { return rdx; }
const Register StringCompareDescriptor::RightRegister() { return rax; }

View File

@ -119,7 +119,7 @@ bytecodes: [
B(TestEqualStrict), R(12),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), U16(130),
B(Wide), B(LdaSmi), U16(129),
B(Star), R(12),
B(LdaConstant), U8(8),
B(Star), R(13),
@ -302,7 +302,7 @@ bytecodes: [
B(TestEqualStrict), R(13),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), U16(130),
B(Wide), B(LdaSmi), U16(129),
B(Star), R(13),
B(LdaConstant), U8(8),
B(Star), R(14),
@ -499,7 +499,7 @@ bytecodes: [
B(TestEqualStrict), R(12),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), U16(130),
B(Wide), B(LdaSmi), U16(129),
B(Star), R(12),
B(LdaConstant), U8(8),
B(Star), R(13),
@ -686,7 +686,7 @@ bytecodes: [
B(TestEqualStrict), R(11),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), U16(130),
B(Wide), B(LdaSmi), U16(129),
B(Star), R(11),
B(LdaConstant), U8(10),
B(Star), R(12),

View File

@ -505,7 +505,7 @@ bytecodes: [
B(TestEqualStrict), R(10),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), U16(130),
B(Wide), B(LdaSmi), U16(129),
B(Star), R(10),
B(LdaConstant), U8(14),
B(Star), R(11),

View File

@ -662,127 +662,71 @@ snippet: "
}
f(1, 1);
"
frame size: 6
frame size: 1
parameter count: 3
bytecode array length: 226
bytecode array length: 107
bytecodes: [
/* 10 E> */ B(StackCheck),
/* 21 S> */ B(Ldar), R(arg0),
B(Star), R(3),
B(Star), R(0),
/* 30 E> */ B(Ldar), R(arg1),
/* 27 E> */ B(TestEqual), R(3),
/* 27 E> */ B(TestEqual), R(0),
B(JumpIfFalse), U8(5),
/* 35 S> */ B(LdaSmi), U8(1),
/* 262 S> */ B(Return),
/* 49 S> */ B(Ldar), R(arg0),
B(Star), R(3),
B(Star), R(0),
/* 59 E> */ B(Ldar), R(arg1),
/* 55 E> */ B(TestEqualStrict), R(3),
/* 55 E> */ B(TestEqualStrict), R(0),
B(JumpIfFalse), U8(5),
/* 64 S> */ B(LdaSmi), U8(1),
/* 262 S> */ B(Return),
/* 78 S> */ B(Ldar), R(arg0),
B(Star), R(3),
B(Star), R(0),
/* 86 E> */ B(Ldar), R(arg1),
/* 84 E> */ B(TestLessThan), R(3),
/* 84 E> */ B(TestLessThan), R(0),
B(JumpIfFalse), U8(5),
/* 91 S> */ B(LdaSmi), U8(1),
/* 262 S> */ B(Return),
/* 105 S> */ B(Ldar), R(arg0),
B(Star), R(3),
B(Star), R(0),
/* 113 E> */ B(Ldar), R(arg1),
/* 111 E> */ B(TestGreaterThan), R(3),
/* 111 E> */ B(TestGreaterThan), R(0),
B(JumpIfFalse), U8(5),
/* 118 S> */ B(LdaSmi), U8(1),
/* 262 S> */ B(Return),
/* 132 S> */ B(Ldar), R(arg0),
B(Star), R(3),
B(Star), R(0),
/* 141 E> */ B(Ldar), R(arg1),
/* 138 E> */ B(TestLessThanOrEqual), R(3),
/* 138 E> */ B(TestLessThanOrEqual), R(0),
B(JumpIfFalse), U8(5),
/* 146 S> */ B(LdaSmi), U8(1),
/* 262 S> */ B(Return),
/* 160 S> */ B(Ldar), R(arg0),
B(Star), R(3),
B(Star), R(0),
/* 169 E> */ B(Ldar), R(arg1),
/* 166 E> */ B(TestGreaterThanOrEqual), R(3),
/* 166 E> */ B(TestGreaterThanOrEqual), R(0),
B(JumpIfFalse), U8(5),
/* 174 S> */ B(LdaSmi), U8(1),
/* 262 S> */ B(Return),
/* 188 S> */ B(Ldar), R(arg0),
B(Star), R(3),
B(Star), R(0),
/* 197 E> */ B(Ldar), R(arg1),
/* 194 E> */ B(TestIn), R(3),
/* 194 E> */ B(TestIn), R(0),
B(JumpIfFalse), U8(5),
/* 202 S> */ B(LdaSmi), U8(1),
/* 262 S> */ B(Return),
/* 216 S> */ B(Ldar), R(arg0),
B(Star), R(0),
/* 233 E> */ B(Ldar), R(arg1),
B(Star), R(1),
B(Star), R(3),
B(InvokeIntrinsic), U16(Runtime::k_IsJSReceiver), R(3), U8(1),
B(JumpIfToBooleanFalse), U8(4),
B(Jump), U8(16),
/* 222 S> */ B(LdaSmi), U8(61),
B(Star), R(3),
B(LdaConstant), U8(0),
B(Star), R(4),
B(CallRuntime), U16(Runtime::kNewTypeError), R(3), U8(2),
/* 222 E> */ B(Throw),
B(Ldar), R(1),
B(Star), R(3),
/* 222 E> */ B(LdaConstant), U8(1),
B(KeyedLoadIC), R(3), U8(1),
B(Star), R(2),
B(Star), R(3),
B(LdaUndefined),
B(TestEqualStrict), R(3),
B(JumpIfFalse), U8(55),
B(Ldar), R(1),
B(TypeOf),
B(Star), R(3),
B(LdaConstant), U8(2),
B(TestEqualStrict), R(3),
B(JumpIfFalse), U8(4),
B(Jump), U8(16),
/* 222 S> */ B(LdaSmi), U8(16),
B(Star), R(3),
B(LdaConstant), U8(0),
B(Star), R(4),
B(CallRuntime), U16(Runtime::kNewTypeError), R(3), U8(2),
/* 222 E> */ B(Throw),
B(CallRuntime), U16(Runtime::k_GetOrdinaryHasInstance), R(0), U8(0),
B(Star), R(2),
B(Star), R(3),
B(Ldar), R(1),
B(Star), R(4),
B(Ldar), R(0),
B(Star), R(5),
B(CallRuntime), U16(Runtime::k_Call), R(3), U8(3),
B(Star), R(2),
B(Jump), U8(23),
B(Ldar), R(2),
B(Star), R(3),
B(Ldar), R(1),
B(Star), R(4),
B(Ldar), R(0),
B(Star), R(5),
B(CallRuntime), U16(Runtime::k_Call), R(3), U8(3),
B(LogicalNot),
B(LogicalNot),
B(Star), R(2),
B(Ldar), R(2),
B(JumpIfToBooleanFalse), U8(5),
/* 222 E> */ B(TestInstanceOf), R(0),
B(JumpIfFalse), U8(5),
/* 238 S> */ B(LdaSmi), U8(1),
/* 262 S> */ B(Return),
/* 252 S> */ B(LdaZero),
/* 262 S> */ B(Return),
]
constant pool: [
InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE,
InstanceType::SYMBOL_TYPE,
InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE,
]
handlers: [
]

View File

@ -1383,11 +1383,6 @@ TEST(InterpreterStrictNotEqual) {
TEST(InterpreterInstanceOf) {
HandleAndZoneScope handles;
// TODO(4447): The new ES6 'instanceof' operator is fully desugared in the
// parser and the Token::INSTANCEOF is not needed anymore. This test only
// makes sense with --no-harmony-instanceof and can be removed once we
// deprecate the ability to switch to old skool ES5 'instanceof' for good.
FLAG_harmony_instanceof = false;
i::Factory* factory = handles.main_isolate()->factory();
Handle<i::String> name = factory->NewStringFromAsciiChecked("cons");
Handle<i::JSFunction> func = factory->NewFunction(name);

View File

@ -1800,14 +1800,10 @@ TEST(NeStrict) {
TEST(InstanceOf) {
const char* errorMsg = FLAG_harmony_instanceof
? "asm: line 0: do-expression encountered\n"
: "asm: line 1: illegal comparison operator\n";
CHECK_FUNC_ERROR(
"function bar() { return (0 instanceof 0)|0; }\n"
"function foo() { bar(); }",
errorMsg);
"asm: line 1: illegal comparison operator\n");
}

View File

@ -0,0 +1,17 @@
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax --harmony-instanceof
function F() {}
var f = new F
var proto = Object.getPrototypeOf(F);
Object.setPrototypeOf(F, null);
F[Symbol.hasInstance] = function(v) { return true };
Object.setPrototypeOf(F, proto);
function foo(x) { return x instanceof F };
%OptimizeFunctionOnNextCall(foo);
assertTrue(foo(1));

View File

@ -0,0 +1,19 @@
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax --harmony-instanceof
function F() {}
var f = new F
function foo(x) { return x instanceof F };
%OptimizeFunctionOnNextCall(foo);
assertFalse(foo(1));
var proto = Object.getPrototypeOf(F);
Object.setPrototypeOf(F, null);
F[Symbol.hasInstance] = function(v) { return true };
Object.setPrototypeOf(F, proto);
assertTrue(foo(1));

View File

@ -48,3 +48,22 @@ assertEquals(Function.prototype[Symbol.hasInstance].call({}, {}), false);
// OrdinaryHasInstance passed a non-object argument returns false.
assertEquals(Function.prototype[Symbol.hasInstance].call(Array, 0), false);
// Cannot assign to @@hasInstance with %FunctionPrototype%.
(function() {
"use strict";
function F() {}
assertThrows(function() { F[Symbol.hasInstance] = (v) => v }, TypeError);
})();
// Check correct invocation of @@hasInstance handler on function instance.
(function() {
function F() {}
var counter = 0;
var proto = Object.getPrototypeOf(F);
Object.setPrototypeOf(F, null);
F[Symbol.hasInstance] = function(v) { ++counter; return true };
Object.setPrototypeOf(F, proto);
assertTrue(1 instanceof F);
assertEquals(1, counter);
})();

View File

@ -147,7 +147,12 @@ test(function() {
}, "Method Set.prototype.add called on incompatible receiver [object Array]",
TypeError);
// kInstanceofFunctionExpected
// kNonCallableInInstanceOfCheck
test(function() {
1 instanceof {};
}, "Right-hand side of 'instanceof' is not callable", TypeError);
// kNonObjectInInstanceOfCheck
test(function() {
1 instanceof 1;
}, "Right-hand side of 'instanceof' is not an object", TypeError);

View File

@ -284,23 +284,6 @@ TEST_F(JSIntrinsicLoweringTest, InlineValueOf) {
AllOf(CaptureEq(&if_false0), IsIfFalse(CaptureEq(&branch0))))));
}
// -----------------------------------------------------------------------------
// %_GetOrdinaryHasInstance
TEST_F(JSIntrinsicLoweringTest, InlineGetOrdinaryHasInstance) {
Node* const context = Parameter(0);
Node* const effect = graph()->start();
Node* const control = graph()->start();
Reduction const r = Reduce(graph()->NewNode(
javascript()->CallRuntime(Runtime::kInlineGetOrdinaryHasInstance, 0),
context, effect, control));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(
r.replacement(),
IsLoadContext(
ContextAccess(0, Context::ORDINARY_HAS_INSTANCE_INDEX, true), _));
}
} // namespace compiler
} // namespace internal
} // namespace v8