Skip to content

Commit 8de4d93

Browse files
bmeurerCommit bot
authored andcommitted
[es6] Introduce spec compliant IsConstructor.
There was already a bit on the Map named "function with prototype", which basically meant that the Map was a map for a JSFunction that could be used as a constructor. Now this CL generalizes that bit to IsConstructor, which says that whatever (Heap)Object you are looking at can be used as a constructor (i.e. the bit is also set for bound functions that can be used as constructors and proxies that have a [[Construct]] internal method). This way we have a single chokepoint for IsConstructor checking, which allows us to get rid of the various ways in which we tried to guess whether something could be used as a constructor or not. Drive-by-fix: Renamed IsConstructor on FunctionKind to IsClassConstructor to resolve the weird name clash, and the IsClassConstructor name also matches the spec. R=jarin@chromium.org, rossberg@chromium.org BUG=v8:4430 LOG=n Review URL: https://codereview.chromium.org/1358423002 Cr-Commit-Position: refs/heads/master@{#30900}
1 parent 5ced12c commit 8de4d93

26 files changed

Lines changed: 311 additions & 225 deletions

src/accessors.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -929,7 +929,7 @@ MUST_USE_RESULT static MaybeHandle<Object> SetFunctionPrototype(
929929

930930
MaybeHandle<Object> Accessors::FunctionSetPrototype(Handle<JSFunction> function,
931931
Handle<Object> prototype) {
932-
DCHECK(function->should_have_prototype());
932+
DCHECK(function->IsConstructor());
933933
Isolate* isolate = function->GetIsolate();
934934
return SetFunctionPrototype(isolate, function, prototype);
935935
}

src/api-natives.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,7 @@ Handle<JSFunction> ApiNatives::CreateApiFunction(
494494
// Mark instance as callable in the map.
495495
if (!obj->instance_call_handler()->IsUndefined()) {
496496
map->set_is_callable();
497+
map->set_is_constructor(true);
497498
}
498499

499500
// Recursively copy parent instance templates' accessors,

src/arm/builtins-arm.cc

Lines changed: 39 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1635,6 +1635,21 @@ void Builtins::Generate_ConstructFunction(MacroAssembler* masm) {
16351635
}
16361636

16371637

1638+
// static
1639+
void Builtins::Generate_ConstructProxy(MacroAssembler* masm) {
1640+
// ----------- S t a t e -------------
1641+
// -- r0 : the number of arguments (not including the receiver)
1642+
// -- r1 : the constructor to call (checked to be a JSFunctionProxy)
1643+
// -- r3 : the original constructor (either the same as the constructor or
1644+
// the JSFunction on which new was invoked initially)
1645+
// -----------------------------------
1646+
1647+
// TODO(neis): This doesn't match the ES6 spec for [[Construct]] on proxies.
1648+
__ ldr(r1, FieldMemOperand(r1, JSFunctionProxy::kConstructTrapOffset));
1649+
__ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET);
1650+
}
1651+
1652+
16381653
// static
16391654
void Builtins::Generate_Construct(MacroAssembler* masm) {
16401655
// ----------- S t a t e -------------
@@ -1644,35 +1659,35 @@ void Builtins::Generate_Construct(MacroAssembler* masm) {
16441659
// the JSFunction on which new was invoked initially)
16451660
// -----------------------------------
16461661

1647-
Label non_callable, non_function;
1648-
__ JumpIfSmi(r1, &non_callable);
1649-
__ CompareObjectType(r1, r4, r5, JS_FUNCTION_TYPE);
1662+
// Check if target has a [[Construct]] internal method.
1663+
Label non_constructor;
1664+
__ JumpIfSmi(r1, &non_constructor);
1665+
__ ldr(r4, FieldMemOperand(r1, HeapObject::kMapOffset));
1666+
__ ldrb(r2, FieldMemOperand(r4, Map::kBitFieldOffset));
1667+
__ tst(r2, Operand(1 << Map::kIsConstructor));
1668+
__ b(eq, &non_constructor);
1669+
1670+
// Dispatch based on instance type.
1671+
__ CompareInstanceType(r4, r5, JS_FUNCTION_TYPE);
16501672
__ Jump(masm->isolate()->builtins()->ConstructFunction(),
16511673
RelocInfo::CODE_TARGET, eq);
16521674
__ cmp(r5, Operand(JS_FUNCTION_PROXY_TYPE));
1653-
__ b(ne, &non_function);
1654-
1655-
// 1. Construct of function proxy.
1656-
// TODO(neis): This doesn't match the ES6 spec for [[Construct]] on proxies.
1657-
__ ldr(r1, FieldMemOperand(r1, JSFunctionProxy::kConstructTrapOffset));
1658-
__ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET);
1675+
__ Jump(masm->isolate()->builtins()->ConstructProxy(), RelocInfo::CODE_TARGET,
1676+
eq);
16591677

1660-
// 2. Construct of something that else, which might have a [[Construct]]
1661-
// internal method (if not we raise an exception).
1662-
__ bind(&non_function);
1663-
// Check if target has a [[Call]] internal method.
1664-
// TODO(bmeurer): This shoud use IsConstructor once available.
1665-
__ ldrb(r4, FieldMemOperand(r4, Map::kBitFieldOffset));
1666-
__ tst(r4, Operand(1 << Map::kIsCallable));
1667-
__ b(eq, &non_callable);
1668-
// Overwrite the original receiver the (original) target.
1669-
__ str(r1, MemOperand(sp, r0, LSL, kPointerSizeLog2));
1670-
// Let the "call_as_constructor_delegate" take care of the rest.
1671-
__ LoadGlobalFunction(Context::CALL_AS_CONSTRUCTOR_DELEGATE_INDEX, r1);
1672-
__ Jump(masm->isolate()->builtins()->CallFunction(), RelocInfo::CODE_TARGET);
1678+
// Called Construct on an exotic Object with a [[Construct]] internal method.
1679+
{
1680+
// Overwrite the original receiver with the (original) target.
1681+
__ str(r1, MemOperand(sp, r0, LSL, kPointerSizeLog2));
1682+
// Let the "call_as_constructor_delegate" take care of the rest.
1683+
__ LoadGlobalFunction(Context::CALL_AS_CONSTRUCTOR_DELEGATE_INDEX, r1);
1684+
__ Jump(masm->isolate()->builtins()->CallFunction(),
1685+
RelocInfo::CODE_TARGET);
1686+
}
16731687

1674-
// 3. Construct of something that is not callable.
1675-
__ bind(&non_callable);
1688+
// Called Construct on an Object that doesn't have a [[Construct]] internal
1689+
// method.
1690+
__ bind(&non_constructor);
16761691
{
16771692
FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL);
16781693
__ Push(r1);

src/arm64/builtins-arm64.cc

Lines changed: 38 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1692,6 +1692,21 @@ void Builtins::Generate_ConstructFunction(MacroAssembler* masm) {
16921692
}
16931693

16941694

1695+
// static
1696+
void Builtins::Generate_ConstructProxy(MacroAssembler* masm) {
1697+
// ----------- S t a t e -------------
1698+
// -- x0 : the number of arguments (not including the receiver)
1699+
// -- x1 : the constructor to call (checked to be a JSFunctionProxy)
1700+
// -- x3 : the original constructor (either the same as the constructor or
1701+
// the JSFunction on which new was invoked initially)
1702+
// -----------------------------------
1703+
1704+
// TODO(neis): This doesn't match the ES6 spec for [[Construct]] on proxies.
1705+
__ Ldr(x1, FieldMemOperand(x1, JSFunctionProxy::kConstructTrapOffset));
1706+
__ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET);
1707+
}
1708+
1709+
16951710
// static
16961711
void Builtins::Generate_Construct(MacroAssembler* masm) {
16971712
// ----------- S t a t e -------------
@@ -1701,34 +1716,34 @@ void Builtins::Generate_Construct(MacroAssembler* masm) {
17011716
// the JSFunction on which new was invoked initially)
17021717
// -----------------------------------
17031718

1704-
Label non_callable, non_function;
1705-
__ JumpIfSmi(x1, &non_callable);
1706-
__ CompareObjectType(x1, x4, x5, JS_FUNCTION_TYPE);
1719+
// Check if target has a [[Construct]] internal method.
1720+
Label non_constructor;
1721+
__ JumpIfSmi(x1, &non_constructor);
1722+
__ Ldr(x4, FieldMemOperand(x1, HeapObject::kMapOffset));
1723+
__ Ldrb(x2, FieldMemOperand(x4, Map::kBitFieldOffset));
1724+
__ TestAndBranchIfAllClear(x2, 1 << Map::kIsConstructor, &non_constructor);
1725+
1726+
// Dispatch based on instance type.
1727+
__ CompareInstanceType(x4, x5, JS_FUNCTION_TYPE);
17071728
__ Jump(masm->isolate()->builtins()->ConstructFunction(),
17081729
RelocInfo::CODE_TARGET, eq);
17091730
__ Cmp(x5, JS_FUNCTION_PROXY_TYPE);
1710-
__ B(ne, &non_function);
1711-
1712-
// 1. Construct of function proxy.
1713-
// TODO(neis): This doesn't match the ES6 spec for [[Construct]] on proxies.
1714-
__ Ldr(x1, FieldMemOperand(x1, JSFunctionProxy::kConstructTrapOffset));
1715-
__ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET);
1731+
__ Jump(masm->isolate()->builtins()->ConstructProxy(), RelocInfo::CODE_TARGET,
1732+
eq);
17161733

1717-
// 2. Construct of something that else, which might have a [[Construct]]
1718-
// internal method (if not we raise an exception).
1719-
__ Bind(&non_function);
1720-
// Check if target has a [[Call]] internal method.
1721-
// TODO(bmeurer): This shoud use IsConstructor once available.
1722-
__ Ldrb(x4, FieldMemOperand(x4, Map::kBitFieldOffset));
1723-
__ TestAndBranchIfAllClear(x4, 1 << Map::kIsCallable, &non_callable);
1724-
// Overwrite the original receiver with the (original) target.
1725-
__ Poke(x1, Operand(x0, LSL, kXRegSizeLog2));
1726-
// Let the "call_as_constructor_delegate" take care of the rest.
1727-
__ LoadGlobalFunction(Context::CALL_AS_CONSTRUCTOR_DELEGATE_INDEX, x1);
1728-
__ Jump(masm->isolate()->builtins()->CallFunction(), RelocInfo::CODE_TARGET);
1734+
// Called Construct on an exotic Object with a [[Construct]] internal method.
1735+
{
1736+
// Overwrite the original receiver with the (original) target.
1737+
__ Poke(x1, Operand(x0, LSL, kXRegSizeLog2));
1738+
// Let the "call_as_constructor_delegate" take care of the rest.
1739+
__ LoadGlobalFunction(Context::CALL_AS_CONSTRUCTOR_DELEGATE_INDEX, x1);
1740+
__ Jump(masm->isolate()->builtins()->CallFunction(),
1741+
RelocInfo::CODE_TARGET);
1742+
}
17291743

1730-
// 3. Construct of something that is not callable.
1731-
__ bind(&non_callable);
1744+
// Called Construct on an Object that doesn't have a [[Construct]] internal
1745+
// method.
1746+
__ bind(&non_constructor);
17321747
{
17331748
FrameScope scope(masm, StackFrame::INTERNAL);
17341749
__ Push(x1);

src/bootstrapper.cc

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -487,7 +487,7 @@ void Genesis::SetFunctionInstanceDescriptor(Handle<Map> map,
487487
Handle<Map> Genesis::CreateSloppyFunctionMap(FunctionMode function_mode) {
488488
Handle<Map> map = factory()->NewMap(JS_FUNCTION_TYPE, JSFunction::kSize);
489489
SetFunctionInstanceDescriptor(map, function_mode);
490-
map->set_function_with_prototype(IsFunctionModeWithPrototype(function_mode));
490+
map->set_is_constructor(IsFunctionModeWithPrototype(function_mode));
491491
map->set_is_callable();
492492
return map;
493493
}
@@ -727,7 +727,7 @@ Handle<Map> Genesis::CreateStrictFunctionMap(
727727
FunctionMode function_mode, Handle<JSFunction> empty_function) {
728728
Handle<Map> map = factory()->NewMap(JS_FUNCTION_TYPE, JSFunction::kSize);
729729
SetStrictFunctionInstanceDescriptor(map, function_mode);
730-
map->set_function_with_prototype(IsFunctionModeWithPrototype(function_mode));
730+
map->set_is_constructor(IsFunctionModeWithPrototype(function_mode));
731731
map->set_is_callable();
732732
Map::SetPrototype(map, empty_function);
733733
return map;
@@ -738,7 +738,7 @@ Handle<Map> Genesis::CreateStrongFunctionMap(
738738
Handle<JSFunction> empty_function, bool is_constructor) {
739739
Handle<Map> map = factory()->NewMap(JS_FUNCTION_TYPE, JSFunction::kSize);
740740
SetStrongFunctionInstanceDescriptor(map);
741-
map->set_function_with_prototype(is_constructor);
741+
map->set_is_constructor(is_constructor);
742742
Map::SetPrototype(map, empty_function);
743743
map->set_is_callable();
744744
map->set_is_extensible(is_constructor);
@@ -1373,7 +1373,7 @@ void Genesis::InitializeGlobal(Handle<GlobalObject> global_object,
13731373
}
13741374
// @@iterator method is added later.
13751375

1376-
map->set_function_with_prototype(true);
1376+
map->set_is_constructor(true);
13771377
map->SetInObjectProperties(2);
13781378
native_context()->set_sloppy_arguments_map(*map);
13791379

@@ -1439,7 +1439,7 @@ void Genesis::InitializeGlobal(Handle<GlobalObject> global_object,
14391439
}
14401440
// @@iterator method is added later.
14411441

1442-
map->set_function_with_prototype(true);
1442+
map->set_is_constructor(true);
14431443
DCHECK_EQ(native_context()->object_function()->prototype(),
14441444
*isolate->initial_object_prototype());
14451445
Map::SetPrototype(map, isolate->initial_object_prototype());

src/builtins.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ enum BuiltinExtraArguments {
7878
V(Call, BUILTIN, UNINITIALIZED, kNoExtraICState) \
7979
\
8080
V(ConstructFunction, BUILTIN, UNINITIALIZED, kNoExtraICState) \
81+
V(ConstructProxy, BUILTIN, UNINITIALIZED, kNoExtraICState) \
8182
V(Construct, BUILTIN, UNINITIALIZED, kNoExtraICState) \
8283
\
8384
V(PushArgsAndCall, BUILTIN, UNINITIALIZED, kNoExtraICState) \
@@ -281,6 +282,8 @@ class Builtins {
281282

282283
// ES6 section 9.2.2 [[Construct]] ( argumentsList, newTarget)
283284
static void Generate_ConstructFunction(MacroAssembler* masm);
285+
// ES6 section 9.5.14 [[Construct]] ( argumentsList, newTarget)
286+
static void Generate_ConstructProxy(MacroAssembler* masm);
284287
// ES6 section 7.3.13 Construct (F, [argumentsList], [newTarget])
285288
static void Generate_Construct(MacroAssembler* masm);
286289

src/contexts.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -513,7 +513,7 @@ class Context: public FixedArray {
513513
: SLOPPY_GENERATOR_FUNCTION_MAP_INDEX;
514514
}
515515

516-
if (IsConstructor(kind)) {
516+
if (IsClassConstructor(kind)) {
517517
// Use strict function map (no own "caller" / "arguments")
518518
return is_strong(language_mode) ? STRONG_CONSTRUCTOR_MAP_INDEX
519519
: STRICT_FUNCTION_MAP_INDEX;

src/factory.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1930,6 +1930,7 @@ Handle<JSProxy> Factory::NewJSFunctionProxy(Handle<Object> handler,
19301930
Handle<Map> map = NewMap(JS_FUNCTION_PROXY_TYPE, JSFunctionProxy::kSize);
19311931
Map::SetPrototype(map, prototype);
19321932
map->set_is_callable();
1933+
map->set_is_constructor(construct_trap->IsCallable());
19331934

19341935
// Allocate the proxy object.
19351936
Handle<JSFunctionProxy> result = New<JSFunctionProxy>(map, NEW_SPACE);
@@ -1991,7 +1992,7 @@ void Factory::ReinitializeJSProxy(Handle<JSProxy> proxy, InstanceType type,
19911992

19921993
// Functions require some minimal initialization.
19931994
if (type == JS_FUNCTION_TYPE) {
1994-
map->set_function_with_prototype(true);
1995+
map->set_is_constructor(true);
19951996
map->set_is_callable();
19961997
Handle<JSFunction> js_function = Handle<JSFunction>::cast(proxy);
19971998
InitializeFunction(js_function, shared.ToHandleChecked(), context);

src/globals.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -997,7 +997,7 @@ inline bool IsSubclassConstructor(FunctionKind kind) {
997997
}
998998

999999

1000-
inline bool IsConstructor(FunctionKind kind) {
1000+
inline bool IsClassConstructor(FunctionKind kind) {
10011001
DCHECK(IsValidFunctionKind(kind));
10021002
return kind &
10031003
(FunctionKind::kBaseConstructor | FunctionKind::kSubclassConstructor |

src/hydrogen.cc

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6418,7 +6418,8 @@ bool HOptimizedGraphBuilder::PropertyAccessInfo::CanAccessMonomorphic() {
64186418
if (!CanInlinePropertyAccess(map_)) return false;
64196419
if (IsJSObjectFieldAccessor()) return IsLoad();
64206420
if (IsJSArrayBufferViewFieldAccessor()) return IsLoad();
6421-
if (map_->function_with_prototype() && !map_->has_non_instance_prototype() &&
6421+
if (map_->IsJSFunctionMap() && map_->is_constructor() &&
6422+
!map_->has_non_instance_prototype() &&
64226423
name_.is_identical_to(isolate()->factory()->prototype_string())) {
64236424
return IsLoad();
64246425
}
@@ -6532,7 +6533,7 @@ HValue* HOptimizedGraphBuilder::BuildMonomorphicAccess(
65326533
}
65336534

65346535
if (info->name().is_identical_to(isolate()->factory()->prototype_string()) &&
6535-
info->map()->function_with_prototype()) {
6536+
info->map()->IsJSFunctionMap() && info->map()->is_constructor()) {
65366537
DCHECK(!info->map()->has_non_instance_prototype());
65376538
return New<HLoadFunctionPrototype>(checked_object);
65386539
}

0 commit comments

Comments
 (0)