Commit 99e7f872 authored by ishell's avatar ishell Committed by Commit bot

[es6] Fix Function and GeneratorFunction built-ins subclassing.

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

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

Cr-Commit-Position: refs/heads/master@{#31708}
parent e4af8a06
......@@ -1116,6 +1116,7 @@ void Genesis::InitializeGlobal(Handle<GlobalObject> global_object,
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);
{ // --- A r r a y ---
Handle<JSFunction> array_function =
......@@ -1951,6 +1952,7 @@ void Bootstrapper::ExportFromRuntime(Isolate* isolate,
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);
}
{ // -- S e t I t e r a t o r
......
......@@ -1318,8 +1318,19 @@ Handle<JSFunction> Factory::NewFunctionFromSharedFunctionInfo(
PretenureFlag pretenure) {
int map_index =
Context::FunctionMapIndex(info->language_mode(), info->kind());
Handle<Map> map(Map::cast(context->native_context()->get(map_index)));
Handle<JSFunction> result = NewFunction(map, info, context, pretenure);
Handle<Map> initial_map(Map::cast(context->native_context()->get(map_index)));
return NewFunctionFromSharedFunctionInfo(initial_map, info, context,
pretenure);
}
Handle<JSFunction> Factory::NewFunctionFromSharedFunctionInfo(
Handle<Map> initial_map, Handle<SharedFunctionInfo> info,
Handle<Context> context, PretenureFlag pretenure) {
DCHECK_EQ(JS_FUNCTION_TYPE, initial_map->instance_type());
Handle<JSFunction> result =
NewFunction(initial_map, info, context, pretenure);
if (info->ic_age() != isolate()->heap()->global_ic_age()) {
info->ResetForNewContext(isolate()->heap()->global_ic_age());
......
......@@ -507,8 +507,11 @@ class Factory final {
bool is_strict = false);
Handle<JSFunction> NewFunctionFromSharedFunctionInfo(
Handle<SharedFunctionInfo> function_info,
Handle<Context> context,
Handle<Map> initial_map, Handle<SharedFunctionInfo> function_info,
Handle<Context> context, PretenureFlag pretenure = TENURED);
Handle<JSFunction> NewFunctionFromSharedFunctionInfo(
Handle<SharedFunctionInfo> function_info, Handle<Context> context,
PretenureFlag pretenure = TENURED);
Handle<JSFunction> NewFunction(Handle<String> name, Handle<Code> code,
......
......@@ -84,9 +84,11 @@ function GeneratorFunctionConstructor(arg1) { // length == 1
var global_proxy = %GlobalProxy(GeneratorFunctionConstructor);
// Compile the string in the constructor and not a helper so that errors
// appear to come from here.
var f = %_CallFunction(global_proxy, %CompileString(source, true));
%FunctionMarkNameShouldPrintAsAnonymous(f);
return f;
var func = %_CallFunction(global_proxy, %CompileString(source, true));
// Set name-should-print-as-anonymous flag on the ShareFunctionInfo and
// ensure that |func| uses correct initial map from |new.target| if
// it's available.
return %CompleteFunctionConstruction(func, GeneratorFunction, new.target);
}
// ----------------------------------------------------------------------------
......
......@@ -1786,9 +1786,11 @@ function FunctionConstructor(arg1) { // length == 1
var global_proxy = %GlobalProxy(FunctionConstructor);
// Compile the string in the constructor and not a helper so that errors
// appear to come from here.
var f = %_CallFunction(global_proxy, %CompileString(source, true));
%FunctionMarkNameShouldPrintAsAnonymous(f);
return f;
var func = %_CallFunction(global_proxy, %CompileString(source, true));
// Set name-should-print-as-anonymous flag on the ShareFunctionInfo and
// ensure that |func| uses correct initial map from |new.target| if
// it's available.
return %CompleteFunctionConstruction(func, GlobalFunction, new.target);
}
......
......@@ -870,6 +870,9 @@ void JSFunction::JSFunctionPrint(std::ostream& os) { // NOLINT
if (has_initial_map()) os << Brief(initial_map());
os << "\n - shared_info = " << Brief(shared());
os << "\n - name = " << Brief(shared()->name());
if (shared()->is_generator()) {
os << "\n - generator";
}
os << "\n - context = " << Brief(context());
if (shared()->bound()) {
os << "\n - bindings = " << Brief(function_bindings());
......
......@@ -47,12 +47,40 @@ RUNTIME_FUNCTION(Runtime_FunctionNameShouldPrintAsAnonymous) {
}
RUNTIME_FUNCTION(Runtime_FunctionMarkNameShouldPrintAsAnonymous) {
RUNTIME_FUNCTION(Runtime_CompleteFunctionConstruction) {
SealHandleScope shs(isolate);
DCHECK(args.length() == 1);
CONVERT_ARG_CHECKED(JSFunction, f, 0);
f->shared()->set_name_should_print_as_anonymous(true);
return isolate->heap()->undefined_value();
DCHECK(args.length() == 3);
CONVERT_ARG_HANDLE_CHECKED(JSFunction, func, 0);
CONVERT_ARG_HANDLE_CHECKED(JSFunction, constructor, 1);
CONVERT_ARG_HANDLE_CHECKED(Object, new_target, 2);
func->shared()->set_name_should_print_as_anonymous(true);
// If new.target is equal to |constructor| then the function |func| created
// is already correctly setup and nothing else should be done here.
// But if new.target is not equal to |constructor| then we are have a
// Function builtin subclassing case and therefore the function |func|
// has wrong initial map. To fix that we create a new function object with
// correct initial map.
if (new_target->IsUndefined() || *constructor == *new_target) {
return *func;
}
// Create a new JSFunction object with correct initial map.
HandleScope handle_scope(isolate);
Handle<JSFunction> original_constructor =
Handle<JSFunction>::cast(new_target);
DCHECK(constructor->has_initial_map());
Handle<Map> initial_map =
JSFunction::EnsureDerivedHasInitialMap(original_constructor, constructor);
Handle<SharedFunctionInfo> shared_info(func->shared(), isolate);
Handle<Context> context(func->context(), isolate);
Handle<JSFunction> result =
isolate->factory()->NewFunctionFromSharedFunctionInfo(
initial_map, shared_info, context, NOT_TENURED);
DCHECK_EQ(func->IsConstructor(), result->IsConstructor());
return *result;
}
......
......@@ -996,24 +996,16 @@ static Object* Runtime_NewObjectHelper(Isolate* isolate,
Compiler::Compile(function, CLEAR_EXCEPTION);
JSFunction::EnsureHasInitialMap(function);
Handle<Map> initial_map =
JSFunction::EnsureDerivedHasInitialMap(original_function, function);
if (initial_map->instance_type() == JS_FUNCTION_TYPE) {
if (function->initial_map()->instance_type() == JS_FUNCTION_TYPE) {
// The 'Function' function ignores the receiver object when
// called using 'new' and creates a new JSFunction object that
// is returned. The receiver object is only used for error
// reporting if an error occurs when constructing the new
// JSFunction. Factory::NewJSObject() should not be used to
// allocate JSFunctions since it does not properly initialize
// the shared part of the function. Since the receiver is
// ignored anyway, we use the global object as the receiver
// instead of a new JSFunction object. This way, errors are
// reported the same way whether or not 'Function' is called
// using 'new'.
return isolate->global_proxy();
// is returned.
return isolate->heap()->undefined_value();
}
Handle<Map> initial_map =
JSFunction::EnsureDerivedHasInitialMap(original_function, function);
Handle<JSObject> result =
isolate->factory()->NewJSObjectFromMap(initial_map, NOT_TENURED, site);
......
......@@ -236,7 +236,7 @@ namespace internal {
F(FunctionGetName, 1, 1) \
F(FunctionSetName, 2, 1) \
F(FunctionNameShouldPrintAsAnonymous, 1, 1) \
F(FunctionMarkNameShouldPrintAsAnonymous, 1, 1) \
F(CompleteFunctionConstruction, 3, 1) \
F(FunctionIsArrow, 1, 1) \
F(FunctionIsConciseMethod, 1, 1) \
F(FunctionRemovePrototype, 1, 1) \
......
......@@ -17,6 +17,30 @@ function checkPrototypeChain(object, constructors) {
}
(function() {
class A extends Function {
constructor(...args) {
assertTrue(%IsConstructCall());
super(...args);
this.a = 42;
}
}
var o = new A("this.foo = 153;");
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);
var oo = new o();
assertEquals(153, oo.foo);
var o1 = new A("return 312;");
assertTrue(%HaveSameMap(o, o1));
})();
(function() {
class A extends Boolean {
constructor(...args) {
......@@ -300,6 +324,42 @@ function TestArraySubclassing(array) {
})();
(function() {
// TODO(ishell): remove once GeneratorFunction is available.
var GeneratorFunction = (function*() {}).__proto__.constructor;
class A extends GeneratorFunction {
constructor(...args) {
assertTrue(%IsConstructCall());
super(...args);
this.a = 42;
}
}
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);
var o = new generator_func();
assertTrue(o instanceof Object);
assertTrue(o instanceof generator_func);
assertEquals("object", typeof o);
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());
var generator_func1 = new A("return 0;");
assertTrue(%HaveSameMap(generator_func, generator_func1));
})();
(function() {
class A extends Boolean {
constructor() {
......
......@@ -10,8 +10,8 @@ try {
new Function({toString:0,valueOf:0});
} catch (e) {
threw = true;
// Ensure that the receiver during "new Function" is the global proxy.
assertEquals(this, e.stack[0].getThis());
// Ensure that the receiver during "new Function" is the undefined value.
assertEquals(undefined, e.stack[0].getThis());
}
assertTrue(threw);
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