Commit dddcd0ac authored by ishell's avatar ishell Committed by Commit bot

Fix Function subclassing.

Function subclasses did not have function properties installed (name, prototype, etc.).
Now when an instance of a Function subclass is created it gets initial map that corresponds
to the language mode of the function body. The language mode dependent maps are cached as
special transitions on initial map of the subclass constructor.

BUG=v8:4597, v8:3101, v8:3330
LOG=Y

Review URL: https://codereview.chromium.org/1510753005

Cr-Commit-Position: refs/heads/master@{#32764}
parent 67f3c80d
...@@ -1081,17 +1081,20 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object, ...@@ -1081,17 +1081,20 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
Handle<JSObject> global(native_context()->global_object()); Handle<JSObject> global(native_context()->global_object());
{ // --- F u n c t i o n ---
{ // Install global Function object
Handle<JSFunction> function_function = Handle<JSFunction> function_function =
InstallFunction(global, "Function", JS_FUNCTION_TYPE, JSFunction::kSize, InstallFunction(global, "Function", JS_FUNCTION_TYPE, JSFunction::kSize,
empty_function, Builtins::kIllegal); empty_function, Builtins::kIllegal);
function_function->initial_map()->set_is_callable(); function_function->set_prototype_or_initial_map(
function_function->initial_map()->set_is_constructor(true); *sloppy_function_map_writable_prototype_);
function_function->shared()->set_construct_stub( function_function->shared()->set_construct_stub(
*isolate->builtins()->JSBuiltinsConstructStub()); *isolate->builtins()->JSBuiltinsConstructStub());
InstallWithIntrinsicDefaultProto(isolate, function_function, InstallWithIntrinsicDefaultProto(isolate, function_function,
Context::FUNCTION_FUNCTION_INDEX); Context::FUNCTION_FUNCTION_INDEX);
sloppy_function_map_writable_prototype_->SetConstructor(*function_function);
strict_function_map_writable_prototype_->SetConstructor(*function_function);
native_context()->strong_function_map()->SetConstructor(*function_function);
} }
{ // --- A r r a y --- { // --- A r r a y ---
...@@ -1872,13 +1875,20 @@ void Bootstrapper::ExportFromRuntime(Isolate* isolate, ...@@ -1872,13 +1875,20 @@ void Bootstrapper::ExportFromRuntime(Isolate* isolate,
InstallFunction(container, "GeneratorFunction", JS_FUNCTION_TYPE, InstallFunction(container, "GeneratorFunction", JS_FUNCTION_TYPE,
JSFunction::kSize, generator_function_prototype, JSFunction::kSize, generator_function_prototype,
Builtins::kIllegal, kUseStrictFunctionMap); Builtins::kIllegal, kUseStrictFunctionMap);
generator_function_function->initial_map()->set_is_callable(); generator_function_function->set_prototype_or_initial_map(
generator_function_function->initial_map()->set_is_constructor(true); native_context->sloppy_generator_function_map());
generator_function_function->shared()->set_construct_stub( generator_function_function->shared()->set_construct_stub(
*isolate->builtins()->JSBuiltinsConstructStub()); *isolate->builtins()->JSBuiltinsConstructStub());
InstallWithIntrinsicDefaultProto( InstallWithIntrinsicDefaultProto(
isolate, generator_function_function, isolate, generator_function_function,
Context::GENERATOR_FUNCTION_FUNCTION_INDEX); Context::GENERATOR_FUNCTION_FUNCTION_INDEX);
native_context->sloppy_generator_function_map()->SetConstructor(
*generator_function_function);
native_context->strict_generator_function_map()->SetConstructor(
*generator_function_function);
native_context->strong_generator_function_map()->SetConstructor(
*generator_function_function);
} }
{ // -- S e t I t e r a t o r { // -- S e t I t e r a t o r
......
...@@ -348,6 +348,7 @@ namespace internal { ...@@ -348,6 +348,7 @@ namespace internal {
V(intl_impl_object_symbol) \ V(intl_impl_object_symbol) \
V(intl_initialized_marker_symbol) \ V(intl_initialized_marker_symbol) \
V(megamorphic_symbol) \ V(megamorphic_symbol) \
V(native_context_index_symbol) \
V(nonexistent_symbol) \ V(nonexistent_symbol) \
V(nonextensible_symbol) \ V(nonextensible_symbol) \
V(normal_ic_symbol) \ V(normal_ic_symbol) \
...@@ -364,10 +365,11 @@ namespace internal { ...@@ -364,10 +365,11 @@ namespace internal {
V(promise_value_symbol) \ V(promise_value_symbol) \
V(sealed_symbol) \ V(sealed_symbol) \
V(stack_trace_symbol) \ V(stack_trace_symbol) \
V(strict_function_transition_symbol) \
V(string_iterator_iterated_string_symbol) \ V(string_iterator_iterated_string_symbol) \
V(string_iterator_next_index_symbol) \ V(string_iterator_next_index_symbol) \
V(uninitialized_symbol) \ V(strong_function_transition_symbol) \
V(native_context_index_symbol) V(uninitialized_symbol)
#define PUBLIC_SYMBOL_LIST(V) \ #define PUBLIC_SYMBOL_LIST(V) \
V(has_instance_symbol, Symbol.hasInstance) \ V(has_instance_symbol, Symbol.hasInstance) \
......
...@@ -1286,6 +1286,10 @@ void TransitionArray::PrintTransitions(std::ostream& os, Object* transitions, ...@@ -1286,6 +1286,10 @@ void TransitionArray::PrintTransitions(std::ostream& os, Object* transitions,
} else if (key == heap->elements_transition_symbol()) { } else if (key == heap->elements_transition_symbol()) {
os << "(transition to " << ElementsKindToString(target->elements_kind()) os << "(transition to " << ElementsKindToString(target->elements_kind())
<< ")"; << ")";
} else if (key == heap->strict_function_transition_symbol()) {
os << " (transition to strict function)";
} else if (key == heap->strong_function_transition_symbol()) {
os << " (transition to strong function)";
} else if (key == heap->observed_symbol()) { } else if (key == heap->observed_symbol()) {
os << " (transition to Object.observe)"; os << " (transition to Object.observe)";
} else { } else {
......
...@@ -8988,9 +8988,17 @@ Handle<Map> Map::CopyInitialMap(Handle<Map> map, int instance_size, ...@@ -8988,9 +8988,17 @@ Handle<Map> Map::CopyInitialMap(Handle<Map> map, int instance_size,
int in_object_properties, int in_object_properties,
int unused_property_fields) { int unused_property_fields) {
#ifdef DEBUG #ifdef DEBUG
Isolate* isolate = map->GetIsolate();
// Strict and strong function maps have Function as a constructor but the
// Function's initial map is a sloppy function map. Same holds for
// GeneratorFunction and its initial map.
Object* constructor = map->GetConstructor(); Object* constructor = map->GetConstructor();
DCHECK(constructor->IsJSFunction()); DCHECK(constructor->IsJSFunction());
DCHECK_EQ(*map, JSFunction::cast(constructor)->initial_map()); DCHECK(*map == JSFunction::cast(constructor)->initial_map() ||
*map == *isolate->strict_function_map() ||
*map == *isolate->strong_function_map() ||
*map == *isolate->strict_generator_function_map() ||
*map == *isolate->strong_generator_function_map());
#endif #endif
// Initial maps must always own their descriptors and it's descriptor array // Initial maps must always own their descriptors and it's descriptor array
// does not contain descriptors that do not belong to the map. // does not contain descriptors that do not belong to the map.
...@@ -9255,6 +9263,57 @@ Handle<Map> Map::CopyAsElementsKind(Handle<Map> map, ElementsKind kind, ...@@ -9255,6 +9263,57 @@ Handle<Map> Map::CopyAsElementsKind(Handle<Map> map, ElementsKind kind,
} }
Handle<Map> Map::AsLanguageMode(Handle<Map> initial_map,
LanguageMode language_mode, FunctionKind kind) {
DCHECK_EQ(JS_FUNCTION_TYPE, initial_map->instance_type());
// Initial map for sloppy mode function is stored in the function
// constructor. Initial maps for strict and strong modes are cached as
// special transitions using |strict_function_transition_symbol| and
// |strong_function_transition_symbol| respectively as a key.
if (language_mode == SLOPPY) return initial_map;
Isolate* isolate = initial_map->GetIsolate();
Factory* factory = isolate->factory();
Handle<Symbol> transition_symbol;
int map_index = Context::FunctionMapIndex(language_mode, kind);
Handle<Map> function_map(
Map::cast(isolate->native_context()->get(map_index)));
STATIC_ASSERT(LANGUAGE_END == 3);
switch (language_mode) {
case STRICT:
transition_symbol = factory->strict_function_transition_symbol();
break;
case STRONG:
transition_symbol = factory->strong_function_transition_symbol();
break;
default:
UNREACHABLE();
break;
}
Map* maybe_transition =
TransitionArray::SearchSpecial(*initial_map, *transition_symbol);
if (maybe_transition != NULL) {
return handle(maybe_transition, isolate);
}
// Create new map taking descriptors from the |function_map| and all
// the other details from the |initial_map|.
Handle<Map> map =
Map::CopyInitialMap(function_map, initial_map->instance_size(),
initial_map->GetInObjectProperties(),
initial_map->unused_property_fields());
map->SetConstructor(initial_map->GetConstructor());
map->set_prototype(initial_map->prototype());
if (TransitionArray::CanHaveMoreTransitions(initial_map)) {
Map::ConnectTransition(initial_map, map, transition_symbol,
SPECIAL_TRANSITION);
}
return map;
}
Handle<Map> Map::CopyForObserved(Handle<Map> map) { Handle<Map> Map::CopyForObserved(Handle<Map> map) {
DCHECK(!map->is_observed()); DCHECK(!map->is_observed());
......
...@@ -5817,6 +5817,11 @@ class Map: public HeapObject { ...@@ -5817,6 +5817,11 @@ class Map: public HeapObject {
ElementsKind kind, ElementsKind kind,
TransitionFlag flag); TransitionFlag flag);
static Handle<Map> AsLanguageMode(Handle<Map> initial_map,
LanguageMode language_mode,
FunctionKind kind);
static Handle<Map> CopyForObserved(Handle<Map> map); static Handle<Map> CopyForObserved(Handle<Map> map);
static Handle<Map> CopyForPreventExtensions(Handle<Map> map, static Handle<Map> CopyForPreventExtensions(Handle<Map> map,
......
...@@ -77,10 +77,13 @@ RUNTIME_FUNCTION(Runtime_CompleteFunctionConstruction) { ...@@ -77,10 +77,13 @@ RUNTIME_FUNCTION(Runtime_CompleteFunctionConstruction) {
JSFunction::GetDerivedMap(isolate, constructor, new_target)); JSFunction::GetDerivedMap(isolate, constructor, new_target));
Handle<SharedFunctionInfo> shared_info(func->shared(), isolate); Handle<SharedFunctionInfo> shared_info(func->shared(), isolate);
Handle<Map> map = Map::AsLanguageMode(
initial_map, shared_info->language_mode(), shared_info->kind());
Handle<Context> context(func->context(), isolate); Handle<Context> context(func->context(), isolate);
Handle<JSFunction> result = Handle<JSFunction> result =
isolate->factory()->NewFunctionFromSharedFunctionInfo( isolate->factory()->NewFunctionFromSharedFunctionInfo(
initial_map, shared_info, context, NOT_TENURED); map, shared_info, context, NOT_TENURED);
DCHECK_EQ(func->IsConstructor(), result->IsConstructor()); DCHECK_EQ(func->IsConstructor(), result->IsConstructor());
return *result; return *result;
} }
......
...@@ -112,6 +112,8 @@ bool TransitionArray::IsSpecialTransition(Name* name) { ...@@ -112,6 +112,8 @@ bool TransitionArray::IsSpecialTransition(Name* name) {
return name == heap->nonextensible_symbol() || return name == heap->nonextensible_symbol() ||
name == heap->sealed_symbol() || name == heap->frozen_symbol() || name == heap->sealed_symbol() || name == heap->frozen_symbol() ||
name == heap->elements_transition_symbol() || name == heap->elements_transition_symbol() ||
name == heap->strict_function_transition_symbol() ||
name == heap->strong_function_transition_symbol() ||
name == heap->observed_symbol(); name == heap->observed_symbol();
} }
#endif #endif
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
// found in the LICENSE file. // found in the LICENSE file.
// Flags: --allow-natives-syntax --harmony-reflect --harmony-regexp-subclass // Flags: --allow-natives-syntax --harmony-reflect --harmony-regexp-subclass
// Flags: --expose-gc // Flags: --expose-gc --strong-mode
"use strict"; "use strict";
...@@ -78,26 +78,71 @@ function checkPrototypeChain(object, constructors) { ...@@ -78,26 +78,71 @@ function checkPrototypeChain(object, constructors) {
constructor(...args) { constructor(...args) {
assertFalse(new.target === undefined); assertFalse(new.target === undefined);
super(...args); super(...args);
// Strong functions are not extensible, so don't add fields.
if (args[args.length - 1].indexOf("use strong") >= 0) {
assertThrows(()=>{ this.a = 10; }, TypeError);
return;
}
this.a = 42; this.a = 42;
this.d = 4.2; this.d = 4.2;
this.o = {foo:153}; this.o = {foo:153};
} }
} }
var sloppy_func = new A("");
var o = new A("this.foo = 113;"); var strict_func = new A("'use strict';");
assertTrue(o instanceof Object); assertNull(sloppy_func.caller);
assertTrue(o instanceof Function); assertThrows("strict_f.caller");
assertTrue(o instanceof A); assertNull(Object.getOwnPropertyDescriptor(sloppy_func, "caller").value);
assertEquals("function", typeof o); assertEquals(undefined, Object.getOwnPropertyDescriptor(strict_func, "caller"));
checkPrototypeChain(o, [A, Function, Object]);
assertEquals(42, o.a); function CheckFunction(func, is_strong) {
assertEquals(4.2, o.d); assertEquals("function", typeof func);
assertEquals(153, o.o.foo); assertTrue(func instanceof Object);
var oo = new o(); assertTrue(func instanceof Function);
assertEquals(113, oo.foo); assertTrue(func instanceof A);
checkPrototypeChain(func, [A, Function, Object]);
var o1 = new A("return 312;"); if (!is_strong) {
assertTrue(%HaveSameMap(o, o1)); assertEquals(42, func.a);
assertEquals(4.2, func.d);
assertEquals(153, func.o.foo);
assertTrue(undefined !== func.prototype);
func.prototype.bar = "func.bar";
var obj = new func();
assertTrue(obj instanceof Object);
assertTrue(obj instanceof func);
assertEquals("object", typeof obj);
assertEquals(113, obj.foo);
assertEquals("func.bar", obj.bar);
delete func.prototype.bar;
}
}
var source = "this.foo = 113;";
// Sloppy function
var sloppy_func = new A(source);
assertTrue(undefined !== sloppy_func.prototype);
CheckFunction(sloppy_func, false);
var sloppy_func1 = new A("return 312;");
assertTrue(%HaveSameMap(sloppy_func, sloppy_func1));
// Strict function
var strict_func = new A("'use strict'; " + source);
assertFalse(%HaveSameMap(strict_func, sloppy_func));
CheckFunction(strict_func, false);
var strict_func1 = new A("'use strict'; return 312;");
assertTrue(%HaveSameMap(strict_func, strict_func1));
// Strong function
var strong_func = new A("'use strong'; " + source);
assertFalse(%HaveSameMap(strong_func, sloppy_func));
assertFalse(%HaveSameMap(strong_func, strict_func));
CheckFunction(strong_func, true);
var strong_func1 = new A("'use strong'; return 312;");
assertTrue(%HaveSameMap(strong_func, strong_func1));
gc(); gc();
})(); })();
...@@ -505,42 +550,86 @@ function TestMapSetSubclassing(container, is_map) { ...@@ -505,42 +550,86 @@ function TestMapSetSubclassing(container, is_map) {
(function() { (function() {
// TODO(ishell): remove once GeneratorFunction is available. var GeneratorFunction = (function*() {}).constructor;
var GeneratorFunction = (function*() {}).__proto__.constructor;
class A extends GeneratorFunction { class A extends GeneratorFunction {
constructor(...args) { constructor(...args) {
assertFalse(new.target === undefined); assertFalse(new.target === undefined);
super(...args); super(...args);
// Strong functions are not extensible, so don't add fields.
if (args[args.length - 1].indexOf("use strong") >= 0) {
assertThrows(()=>{ this.a = 10; }, TypeError);
return;
}
this.a = 42; this.a = 42;
this.d = 4.2; this.d = 4.2;
this.o = {foo:153}; this.o = {foo:153};
} }
} }
var generator_func = new A("var index = 0; while (index < 5) { yield ++index; }"); var sloppy_func = new A("yield 153;");
assertTrue(generator_func instanceof Object); var strict_func = new A("'use strict'; yield 153;");
assertTrue(generator_func instanceof Function); // Unfortunately the difference is not observable from outside.
assertTrue(generator_func instanceof GeneratorFunction); assertThrows("sloppy_func.caller");
assertTrue(generator_func instanceof A); assertThrows("strict_f.caller");
assertEquals("function", typeof generator_func); assertEquals(undefined, Object.getOwnPropertyDescriptor(sloppy_func, "caller"));
checkPrototypeChain(generator_func, [A, GeneratorFunction, Function, Object]); assertEquals(undefined, Object.getOwnPropertyDescriptor(strict_func, "caller"));
assertEquals(42, generator_func.a);
assertEquals(4.2, generator_func.d); function CheckFunction(func, is_strong) {
assertEquals(153, generator_func.o.foo); assertEquals("function", typeof func);
assertTrue(func instanceof Object);
var o = new generator_func(); assertTrue(func instanceof Function);
assertTrue(o instanceof Object); assertTrue(func instanceof GeneratorFunction);
assertTrue(o instanceof generator_func); assertTrue(func instanceof A);
assertEquals("object", typeof o); checkPrototypeChain(func, [A, GeneratorFunction, Function, Object]);
if (!is_strong) {
assertPropertiesEqual({done: false, value: 1}, o.next()); assertEquals(42, func.a);
assertPropertiesEqual({done: false, value: 2}, o.next()); assertEquals(4.2, func.d);
assertPropertiesEqual({done: false, value: 3}, o.next()); assertEquals(153, func.o.foo);
assertPropertiesEqual({done: false, value: 4}, o.next());
assertPropertiesEqual({done: false, value: 5}, o.next()); assertTrue(undefined !== func.prototype);
assertPropertiesEqual({done: true, value: undefined}, o.next()); func.prototype.bar = "func.bar";
var obj = func(); // Generator object.
var generator_func1 = new A("return 0;"); assertTrue(obj instanceof Object);
assertTrue(%HaveSameMap(generator_func, generator_func1)); assertTrue(obj instanceof func);
assertEquals("object", typeof obj);
assertEquals("func.bar", obj.bar);
delete func.prototype.bar;
assertPropertiesEqual({done: false, value: 1}, obj.next());
assertPropertiesEqual({done: false, value: 1}, obj.next());
assertPropertiesEqual({done: false, value: 2}, obj.next());
assertPropertiesEqual({done: false, value: 3}, obj.next());
assertPropertiesEqual({done: false, value: 5}, obj.next());
assertPropertiesEqual({done: false, value: 8}, obj.next());
assertPropertiesEqual({done: true, value: undefined}, obj.next());
}
}
var source = "yield 1; yield 1; yield 2; yield 3; yield 5; yield 8;";
// Sloppy generator function
var sloppy_func = new A(source);
assertTrue(undefined !== sloppy_func.prototype);
CheckFunction(sloppy_func, false);
var sloppy_func1 = new A("yield 312;");
assertTrue(%HaveSameMap(sloppy_func, sloppy_func1));
// Strict generator function
var strict_func = new A("'use strict'; " + source);
assertFalse(%HaveSameMap(strict_func, sloppy_func));
CheckFunction(strict_func, false);
var strict_func1 = new A("'use strict'; yield 312;");
assertTrue(%HaveSameMap(strict_func, strict_func1));
// Strong generator function
var strong_func = new A("'use strong'; " + source);
assertFalse(%HaveSameMap(strong_func, sloppy_func));
assertFalse(%HaveSameMap(strong_func, strict_func));
CheckFunction(strong_func, true);
var strong_func1 = new A("'use strong'; yield 312;");
assertTrue(%HaveSameMap(strong_func, strong_func1));
gc(); gc();
})(); })();
......
...@@ -299,8 +299,12 @@ ...@@ -299,8 +299,12 @@
"EvalError", "EvalError",
"Float32Array", "Float32Array",
"Float64Array", "Float64Array",
"Function", ["Function", ["return 153;"]],
"((function*(){}).constructor)", // GeneratorFunction ["Function", ["'use strict'; return 153;"]],
["Function", ["'use strong'; return 153;"]],
["((function*(){}).constructor)", ["yield 153;"]], // GeneratorFunction
["((function*(){}).constructor)", ["'use strict'; yield 153;"]],
["((function*(){}).constructor)", ["'use strong'; yield 153;"]],
"Int8Array", "Int8Array",
"Int16Array", "Int16Array",
"Int32Array", "Int32Array",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment