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,
Handle<JSObject> global(native_context()->global_object());
{ // Install global Function object
{ // --- F u n c t i o n ---
Handle<JSFunction> function_function =
InstallFunction(global, "Function", JS_FUNCTION_TYPE, JSFunction::kSize,
empty_function, Builtins::kIllegal);
function_function->initial_map()->set_is_callable();
function_function->initial_map()->set_is_constructor(true);
function_function->set_prototype_or_initial_map(
*sloppy_function_map_writable_prototype_);
function_function->shared()->set_construct_stub(
*isolate->builtins()->JSBuiltinsConstructStub());
InstallWithIntrinsicDefaultProto(isolate, function_function,
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 ---
......@@ -1872,13 +1875,20 @@ void Bootstrapper::ExportFromRuntime(Isolate* isolate,
InstallFunction(container, "GeneratorFunction", JS_FUNCTION_TYPE,
JSFunction::kSize, generator_function_prototype,
Builtins::kIllegal, kUseStrictFunctionMap);
generator_function_function->initial_map()->set_is_callable();
generator_function_function->initial_map()->set_is_constructor(true);
generator_function_function->set_prototype_or_initial_map(
native_context->sloppy_generator_function_map());
generator_function_function->shared()->set_construct_stub(
*isolate->builtins()->JSBuiltinsConstructStub());
InstallWithIntrinsicDefaultProto(
isolate, generator_function_function,
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
......
......@@ -348,6 +348,7 @@ namespace internal {
V(intl_impl_object_symbol) \
V(intl_initialized_marker_symbol) \
V(megamorphic_symbol) \
V(native_context_index_symbol) \
V(nonexistent_symbol) \
V(nonextensible_symbol) \
V(normal_ic_symbol) \
......@@ -364,10 +365,11 @@ namespace internal {
V(promise_value_symbol) \
V(sealed_symbol) \
V(stack_trace_symbol) \
V(strict_function_transition_symbol) \
V(string_iterator_iterated_string_symbol) \
V(string_iterator_next_index_symbol) \
V(uninitialized_symbol) \
V(native_context_index_symbol)
V(strong_function_transition_symbol) \
V(uninitialized_symbol)
#define PUBLIC_SYMBOL_LIST(V) \
V(has_instance_symbol, Symbol.hasInstance) \
......
......@@ -1286,6 +1286,10 @@ void TransitionArray::PrintTransitions(std::ostream& os, Object* transitions,
} else if (key == heap->elements_transition_symbol()) {
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()) {
os << " (transition to Object.observe)";
} else {
......
......@@ -8988,9 +8988,17 @@ Handle<Map> Map::CopyInitialMap(Handle<Map> map, int instance_size,
int in_object_properties,
int unused_property_fields) {
#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();
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
// Initial maps must always own their descriptors and it's descriptor array
// does not contain descriptors that do not belong to the map.
......@@ -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) {
DCHECK(!map->is_observed());
......
......@@ -5817,6 +5817,11 @@ class Map: public HeapObject {
ElementsKind kind,
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> CopyForPreventExtensions(Handle<Map> map,
......
......@@ -77,10 +77,13 @@ RUNTIME_FUNCTION(Runtime_CompleteFunctionConstruction) {
JSFunction::GetDerivedMap(isolate, constructor, new_target));
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<JSFunction> result =
isolate->factory()->NewFunctionFromSharedFunctionInfo(
initial_map, shared_info, context, NOT_TENURED);
map, shared_info, context, NOT_TENURED);
DCHECK_EQ(func->IsConstructor(), result->IsConstructor());
return *result;
}
......
......@@ -112,6 +112,8 @@ bool TransitionArray::IsSpecialTransition(Name* name) {
return name == heap->nonextensible_symbol() ||
name == heap->sealed_symbol() || name == heap->frozen_symbol() ||
name == heap->elements_transition_symbol() ||
name == heap->strict_function_transition_symbol() ||
name == heap->strong_function_transition_symbol() ||
name == heap->observed_symbol();
}
#endif
......
......@@ -3,7 +3,7 @@
// found in the LICENSE file.
// Flags: --allow-natives-syntax --harmony-reflect --harmony-regexp-subclass
// Flags: --expose-gc
// Flags: --expose-gc --strong-mode
"use strict";
......@@ -78,26 +78,71 @@ function checkPrototypeChain(object, constructors) {
constructor(...args) {
assertFalse(new.target === undefined);
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.d = 4.2;
this.o = {foo:153};
}
}
var sloppy_func = new A("");
var strict_func = new A("'use strict';");
assertNull(sloppy_func.caller);
assertThrows("strict_f.caller");
assertNull(Object.getOwnPropertyDescriptor(sloppy_func, "caller").value);
assertEquals(undefined, Object.getOwnPropertyDescriptor(strict_func, "caller"));
function CheckFunction(func, is_strong) {
assertEquals("function", typeof func);
assertTrue(func instanceof Object);
assertTrue(func instanceof Function);
assertTrue(func instanceof A);
checkPrototypeChain(func, [A, Function, Object]);
if (!is_strong) {
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 o = new A("this.foo = 113;");
assertTrue(o instanceof Object);
assertTrue(o instanceof Function);
assertTrue(o instanceof A);
assertEquals("function", typeof o);
checkPrototypeChain(o, [A, Function, Object]);
assertEquals(42, o.a);
assertEquals(4.2, o.d);
assertEquals(153, o.o.foo);
var oo = new o();
assertEquals(113, oo.foo);
var source = "this.foo = 113;";
var o1 = new A("return 312;");
assertTrue(%HaveSameMap(o, o1));
// 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();
})();
......@@ -505,42 +550,86 @@ function TestMapSetSubclassing(container, is_map) {
(function() {
// TODO(ishell): remove once GeneratorFunction is available.
var GeneratorFunction = (function*() {}).__proto__.constructor;
var GeneratorFunction = (function*() {}).constructor;
class A extends GeneratorFunction {
constructor(...args) {
assertFalse(new.target === undefined);
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.d = 4.2;
this.o = {foo:153};
}
}
var generator_func = new A("var index = 0; while (index < 5) { yield ++index; }");
assertTrue(generator_func instanceof Object);
assertTrue(generator_func instanceof Function);
assertTrue(generator_func instanceof GeneratorFunction);
assertTrue(generator_func instanceof A);
assertEquals("function", typeof generator_func);
checkPrototypeChain(generator_func, [A, GeneratorFunction, Function, Object]);
assertEquals(42, generator_func.a);
assertEquals(4.2, generator_func.d);
assertEquals(153, generator_func.o.foo);
var o = new generator_func();
assertTrue(o instanceof Object);
assertTrue(o instanceof generator_func);
assertEquals("object", typeof o);
var sloppy_func = new A("yield 153;");
var strict_func = new A("'use strict'; yield 153;");
// Unfortunately the difference is not observable from outside.
assertThrows("sloppy_func.caller");
assertThrows("strict_f.caller");
assertEquals(undefined, Object.getOwnPropertyDescriptor(sloppy_func, "caller"));
assertEquals(undefined, Object.getOwnPropertyDescriptor(strict_func, "caller"));
function CheckFunction(func, is_strong) {
assertEquals("function", typeof func);
assertTrue(func instanceof Object);
assertTrue(func instanceof Function);
assertTrue(func instanceof GeneratorFunction);
assertTrue(func instanceof A);
checkPrototypeChain(func, [A, GeneratorFunction, Function, Object]);
if (!is_strong) {
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 = func(); // Generator object.
assertTrue(obj instanceof Object);
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));
assertPropertiesEqual({done: false, value: 1}, o.next());
assertPropertiesEqual({done: false, value: 2}, o.next());
assertPropertiesEqual({done: false, value: 3}, o.next());
assertPropertiesEqual({done: false, value: 4}, o.next());
assertPropertiesEqual({done: false, value: 5}, o.next());
assertPropertiesEqual({done: true, value: undefined}, o.next());
// 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 generator_func1 = new A("return 0;");
assertTrue(%HaveSameMap(generator_func, generator_func1));
var strong_func1 = new A("'use strong'; yield 312;");
assertTrue(%HaveSameMap(strong_func, strong_func1));
gc();
})();
......
......@@ -299,8 +299,12 @@
"EvalError",
"Float32Array",
"Float64Array",
"Function",
"((function*(){}).constructor)", // GeneratorFunction
["Function", ["return 153;"]],
["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",
"Int16Array",
"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