[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:
parent
565730666e
commit
551e0aa11b
@ -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 -------------
|
||||
|
@ -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();
|
||||
|
@ -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; }
|
||||
|
||||
|
@ -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 -------------
|
||||
|
@ -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);
|
||||
|
@ -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; }
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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 )
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
|
@ -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:
|
||||
|
@ -1213,7 +1213,6 @@ void BytecodeGraphBuilder::VisitTestIn() {
|
||||
}
|
||||
|
||||
void BytecodeGraphBuilder::VisitTestInstanceOf() {
|
||||
DCHECK(!FLAG_harmony_instanceof);
|
||||
BuildCompareOp(javascript()->InstanceOf());
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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()));
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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());
|
||||
|
@ -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) {
|
||||
|
@ -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());
|
||||
|
@ -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());
|
||||
|
@ -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,
|
||||
|
@ -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());
|
||||
|
@ -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()) {
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
||||
|
@ -2286,7 +2286,6 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor {
|
||||
F(RegExpSource) \
|
||||
F(NumberToString) \
|
||||
F(DebugIsActive) \
|
||||
F(GetOrdinaryHasInstance) \
|
||||
/* Typed Arrays */ \
|
||||
F(TypedArrayInitialize) \
|
||||
F(MaxSmi) \
|
||||
|
@ -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());
|
||||
|
@ -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());
|
||||
|
@ -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,
|
||||
|
@ -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());
|
||||
|
@ -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());
|
||||
|
@ -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) {
|
||||
|
@ -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());
|
||||
|
@ -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());
|
||||
|
@ -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) {
|
||||
|
@ -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());
|
||||
|
@ -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());
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
|
@ -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 =
|
||||
|
@ -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 =
|
||||
|
@ -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);
|
||||
|
@ -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 =
|
||||
|
@ -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 =
|
||||
|
@ -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 =
|
||||
|
@ -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 =
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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 -------------
|
||||
|
@ -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
|
||||
|
||||
|
@ -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; }
|
||||
|
||||
|
@ -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()};
|
||||
|
@ -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(
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
||||
|
@ -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());
|
||||
|
@ -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.
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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, \
|
||||
|
@ -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 -------------
|
||||
|
@ -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();
|
||||
|
@ -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; }
|
||||
|
||||
|
@ -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 -------------
|
||||
|
@ -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();
|
||||
|
@ -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; }
|
||||
|
||||
|
@ -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()) {
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
@ -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();
|
||||
|
@ -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_;
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
|
@ -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
|
||||
|
@ -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) \
|
||||
|
@ -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 -------------
|
||||
|
@ -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
|
||||
|
||||
|
@ -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; }
|
||||
|
||||
|
@ -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),
|
||||
|
@ -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),
|
||||
|
@ -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: [
|
||||
]
|
||||
|
@ -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);
|
||||
|
@ -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");
|
||||
}
|
||||
|
||||
|
||||
|
17
test/mjsunit/compiler/optimized-instanceof-1.js
Normal file
17
test/mjsunit/compiler/optimized-instanceof-1.js
Normal 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));
|
19
test/mjsunit/compiler/optimized-instanceof-2.js
Normal file
19
test/mjsunit/compiler/optimized-instanceof-2.js
Normal 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));
|
@ -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);
|
||||
})();
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user