Have one, long-lived map for bound functions.

This avoids creating a new map for every bound function. Bonus: some cleanup in Runtime_FunctionBindArguments.

R=verwaest@chromium.org

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

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@21839 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 9dd1f03c
......@@ -5525,7 +5525,7 @@ class Internals {
static const int kJSObjectHeaderSize = 3 * kApiPointerSize;
static const int kFixedArrayHeaderSize = 2 * kApiPointerSize;
static const int kContextHeaderSize = 2 * kApiPointerSize;
static const int kContextEmbedderDataIndex = 75;
static const int kContextEmbedderDataIndex = 76;
static const int kFullStringRepresentationMask = 0x07;
static const int kStringEncodingMask = 0x4;
static const int kExternalTwoByteRepresentationTag = 0x02;
......
......@@ -262,24 +262,32 @@ class Genesis BASE_EMBEDDED {
void TransferNamedProperties(Handle<JSObject> from, Handle<JSObject> to);
void TransferIndexedProperties(Handle<JSObject> from, Handle<JSObject> to);
enum PrototypePropertyMode {
DONT_ADD_PROTOTYPE,
ADD_READONLY_PROTOTYPE,
ADD_WRITEABLE_PROTOTYPE
enum FunctionMode {
// With prototype.
FUNCTION_WITH_WRITEABLE_PROTOTYPE,
FUNCTION_WITH_READONLY_PROTOTYPE,
// Without prototype.
FUNCTION_WITHOUT_PROTOTYPE,
BOUND_FUNCTION
};
Handle<Map> CreateFunctionMap(PrototypePropertyMode prototype_mode);
static bool IsFunctionModeWithPrototype(FunctionMode function_mode) {
return (function_mode == FUNCTION_WITH_WRITEABLE_PROTOTYPE ||
function_mode == FUNCTION_WITH_READONLY_PROTOTYPE);
}
Handle<Map> CreateFunctionMap(FunctionMode function_mode);
void SetFunctionInstanceDescriptor(Handle<Map> map,
PrototypePropertyMode prototypeMode);
FunctionMode function_mode);
void MakeFunctionInstancePrototypeWritable();
Handle<Map> CreateStrictFunctionMap(
PrototypePropertyMode prototype_mode,
FunctionMode function_mode,
Handle<JSFunction> empty_function);
void SetStrictFunctionInstanceDescriptor(Handle<Map> map,
PrototypePropertyMode propertyMode);
FunctionMode function_mode);
static bool CompileBuiltin(Isolate* isolate, int index);
static bool CompileExperimentalBuiltin(Isolate* isolate, int index);
......@@ -382,8 +390,8 @@ static Handle<JSFunction> InstallFunction(Handle<JSObject> target,
void Genesis::SetFunctionInstanceDescriptor(
Handle<Map> map, PrototypePropertyMode prototypeMode) {
int size = (prototypeMode == DONT_ADD_PROTOTYPE) ? 4 : 5;
Handle<Map> map, FunctionMode function_mode) {
int size = IsFunctionModeWithPrototype(function_mode) ? 5 : 4;
Map::EnsureDescriptorSlack(map, size);
PropertyAttributes attribs = static_cast<PropertyAttributes>(
......@@ -417,8 +425,8 @@ void Genesis::SetFunctionInstanceDescriptor(
caller, attribs);
map->AppendDescriptor(&d);
}
if (prototypeMode != DONT_ADD_PROTOTYPE) {
if (prototypeMode == ADD_WRITEABLE_PROTOTYPE) {
if (IsFunctionModeWithPrototype(function_mode)) {
if (function_mode == FUNCTION_WITH_WRITEABLE_PROTOTYPE) {
attribs = static_cast<PropertyAttributes>(attribs & ~READ_ONLY);
}
Handle<AccessorInfo> prototype =
......@@ -430,10 +438,10 @@ void Genesis::SetFunctionInstanceDescriptor(
}
Handle<Map> Genesis::CreateFunctionMap(PrototypePropertyMode prototype_mode) {
Handle<Map> Genesis::CreateFunctionMap(FunctionMode function_mode) {
Handle<Map> map = factory()->NewMap(JS_FUNCTION_TYPE, JSFunction::kSize);
SetFunctionInstanceDescriptor(map, prototype_mode);
map->set_function_with_prototype(prototype_mode != DONT_ADD_PROTOTYPE);
SetFunctionInstanceDescriptor(map, function_mode);
map->set_function_with_prototype(IsFunctionModeWithPrototype(function_mode));
return map;
}
......@@ -445,14 +453,15 @@ Handle<JSFunction> Genesis::CreateEmptyFunction(Isolate* isolate) {
// Functions with this map will not have a 'prototype' property, and
// can not be used as constructors.
Handle<Map> function_without_prototype_map =
CreateFunctionMap(DONT_ADD_PROTOTYPE);
CreateFunctionMap(FUNCTION_WITHOUT_PROTOTYPE);
native_context()->set_sloppy_function_without_prototype_map(
*function_without_prototype_map);
// Allocate the function map. This map is temporary, used only for processing
// of builtins.
// Later the map is replaced with writable prototype map, allocated below.
Handle<Map> function_map = CreateFunctionMap(ADD_READONLY_PROTOTYPE);
Handle<Map> function_map =
CreateFunctionMap(FUNCTION_WITH_READONLY_PROTOTYPE);
native_context()->set_sloppy_function_map(*function_map);
native_context()->set_sloppy_function_with_readonly_prototype_map(
*function_map);
......@@ -460,7 +469,7 @@ Handle<JSFunction> Genesis::CreateEmptyFunction(Isolate* isolate) {
// The final map for functions. Writeable prototype.
// This map is installed in MakeFunctionInstancePrototypeWritable.
sloppy_function_map_writable_prototype_ =
CreateFunctionMap(ADD_WRITEABLE_PROTOTYPE);
CreateFunctionMap(FUNCTION_WITH_WRITEABLE_PROTOTYPE);
Factory* factory = isolate->factory();
......@@ -514,7 +523,8 @@ Handle<JSFunction> Genesis::CreateEmptyFunction(Isolate* isolate) {
sloppy_function_map_writable_prototype_->set_prototype(*empty_function);
// Allocate the function map first and then patch the prototype later
Handle<Map> empty_function_map = CreateFunctionMap(DONT_ADD_PROTOTYPE);
Handle<Map> empty_function_map =
CreateFunctionMap(FUNCTION_WITHOUT_PROTOTYPE);
empty_function_map->set_prototype(
native_context()->object_function()->prototype());
empty_function->set_map(*empty_function_map);
......@@ -523,8 +533,8 @@ Handle<JSFunction> Genesis::CreateEmptyFunction(Isolate* isolate) {
void Genesis::SetStrictFunctionInstanceDescriptor(
Handle<Map> map, PrototypePropertyMode prototypeMode) {
int size = (prototypeMode == DONT_ADD_PROTOTYPE) ? 4 : 5;
Handle<Map> map, FunctionMode function_mode) {
int size = IsFunctionModeWithPrototype(function_mode) ? 5 : 4;
Map::EnsureDescriptorSlack(map, size);
Handle<AccessorPair> arguments(factory()->NewAccessorPair());
......@@ -534,9 +544,17 @@ void Genesis::SetStrictFunctionInstanceDescriptor(
PropertyAttributes ro_attribs =
static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE | READ_ONLY);
Handle<AccessorInfo> length =
Accessors::FunctionLengthInfo(isolate(), ro_attribs);
{ // Add length.
// Add length.
if (function_mode == BOUND_FUNCTION) {
Handle<String> length_string = isolate()->factory()->length_string();
FieldDescriptor d(length_string, 0, ro_attribs, Representation::Tagged());
map->AppendDescriptor(&d);
} else {
ASSERT(function_mode == FUNCTION_WITH_WRITEABLE_PROTOTYPE ||
function_mode == FUNCTION_WITH_READONLY_PROTOTYPE ||
function_mode == FUNCTION_WITHOUT_PROTOTYPE);
Handle<AccessorInfo> length =
Accessors::FunctionLengthInfo(isolate(), ro_attribs);
CallbacksDescriptor d(Handle<Name>(Name::cast(length->name())),
length, ro_attribs);
map->AppendDescriptor(&d);
......@@ -557,10 +575,11 @@ void Genesis::SetStrictFunctionInstanceDescriptor(
CallbacksDescriptor d(factory()->caller_string(), caller, rw_attribs);
map->AppendDescriptor(&d);
}
if (prototypeMode != DONT_ADD_PROTOTYPE) {
if (IsFunctionModeWithPrototype(function_mode)) {
// Add prototype.
PropertyAttributes attribs =
prototypeMode == ADD_WRITEABLE_PROTOTYPE ? rw_attribs : ro_attribs;
function_mode == FUNCTION_WITH_WRITEABLE_PROTOTYPE ? rw_attribs
: ro_attribs;
Handle<AccessorInfo> prototype =
Accessors::FunctionPrototypeInfo(isolate(), attribs);
CallbacksDescriptor d(Handle<Name>(Name::cast(prototype->name())),
......@@ -605,11 +624,11 @@ Handle<JSFunction> Genesis::GetGeneratorPoisonFunction() {
Handle<Map> Genesis::CreateStrictFunctionMap(
PrototypePropertyMode prototype_mode,
FunctionMode function_mode,
Handle<JSFunction> empty_function) {
Handle<Map> map = factory()->NewMap(JS_FUNCTION_TYPE, JSFunction::kSize);
SetStrictFunctionInstanceDescriptor(map, prototype_mode);
map->set_function_with_prototype(prototype_mode != DONT_ADD_PROTOTYPE);
SetStrictFunctionInstanceDescriptor(map, function_mode);
map->set_function_with_prototype(IsFunctionModeWithPrototype(function_mode));
map->set_prototype(*empty_function);
return map;
}
......@@ -618,7 +637,7 @@ Handle<Map> Genesis::CreateStrictFunctionMap(
void Genesis::CreateStrictModeFunctionMaps(Handle<JSFunction> empty) {
// Allocate map for the prototype-less strict mode instances.
Handle<Map> strict_function_without_prototype_map =
CreateStrictFunctionMap(DONT_ADD_PROTOTYPE, empty);
CreateStrictFunctionMap(FUNCTION_WITHOUT_PROTOTYPE, empty);
native_context()->set_strict_function_without_prototype_map(
*strict_function_without_prototype_map);
......@@ -626,18 +645,23 @@ void Genesis::CreateStrictModeFunctionMaps(Handle<JSFunction> empty) {
// only for processing of builtins.
// Later the map is replaced with writable prototype map, allocated below.
Handle<Map> strict_function_map =
CreateStrictFunctionMap(ADD_READONLY_PROTOTYPE, empty);
CreateStrictFunctionMap(FUNCTION_WITH_READONLY_PROTOTYPE, empty);
native_context()->set_strict_function_map(*strict_function_map);
// The final map for the strict mode functions. Writeable prototype.
// This map is installed in MakeFunctionInstancePrototypeWritable.
strict_function_map_writable_prototype_ =
CreateStrictFunctionMap(ADD_WRITEABLE_PROTOTYPE, empty);
CreateStrictFunctionMap(FUNCTION_WITH_WRITEABLE_PROTOTYPE, empty);
// Special map for bound functions.
Handle<Map> bound_function_map =
CreateStrictFunctionMap(BOUND_FUNCTION, empty);
native_context()->set_bound_function_map(*bound_function_map);
// Complete the callbacks.
PoisonArgumentsAndCaller(strict_function_without_prototype_map);
PoisonArgumentsAndCaller(strict_function_map);
PoisonArgumentsAndCaller(strict_function_map_writable_prototype_);
PoisonArgumentsAndCaller(bound_function_map);
}
......
......@@ -130,6 +130,7 @@ enum BindingFlags {
sloppy_function_without_prototype_map) \
V(STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX, Map, \
strict_function_without_prototype_map) \
V(BOUND_FUNCTION_MAP_INDEX, Map, bound_function_map) \
V(REGEXP_RESULT_MAP_INDEX, Map, regexp_result_map)\
V(SLOPPY_ARGUMENTS_BOILERPLATE_INDEX, JSObject, \
sloppy_arguments_boilerplate) \
......@@ -271,6 +272,7 @@ class Context: public FixedArray {
STRICT_FUNCTION_MAP_INDEX,
SLOPPY_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX,
STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX,
BOUND_FUNCTION_MAP_INDEX,
INITIAL_OBJECT_PROTOTYPE_INDEX,
INITIAL_ARRAY_PROTOTYPE_INDEX,
BOOLEAN_FUNCTION_INDEX,
......
......@@ -8192,8 +8192,9 @@ RUNTIME_FUNCTION(Runtime_FunctionBindArguments) {
HandleScope scope(isolate);
ASSERT(args.length() == 4);
CONVERT_ARG_HANDLE_CHECKED(JSFunction, bound_function, 0);
RUNTIME_ASSERT(args[3]->IsNumber());
Handle<Object> bindee = args.at<Object>(1);
CONVERT_ARG_HANDLE_CHECKED(Object, bindee, 1);
CONVERT_ARG_HANDLE_CHECKED(Object, this_object, 2);
CONVERT_NUMBER_ARG_HANDLE_CHECKED(new_length, 3);
// TODO(lrn): Create bound function in C++ code from premade shared info.
bound_function->shared()->set_bound(true);
......@@ -8203,10 +8204,10 @@ RUNTIME_FUNCTION(Runtime_FunctionBindArguments) {
GetCallerArguments(isolate, 0, &argc);
// Don't count the this-arg.
if (argc > 0) {
RUNTIME_ASSERT(*arguments[0] == args[2]);
RUNTIME_ASSERT(arguments[0].is_identical_to(this_object));
argc--;
} else {
RUNTIME_ASSERT(args[2]->IsUndefined());
RUNTIME_ASSERT(this_object->IsUndefined());
}
// Initialize array of bindings (function, this, and any existing arguments
// if the function was already bound).
......@@ -8228,7 +8229,7 @@ RUNTIME_FUNCTION(Runtime_FunctionBindArguments) {
int array_size = JSFunction::kBoundArgumentsStartIndex + argc;
new_bindings = isolate->factory()->NewFixedArray(array_size);
new_bindings->set(JSFunction::kBoundFunctionIndex, *bindee);
new_bindings->set(JSFunction::kBoundThisIndex, args[2]);
new_bindings->set(JSFunction::kBoundThisIndex, *this_object);
i = 2;
}
// Copy arguments, skipping the first which is "this_arg".
......@@ -8239,13 +8240,17 @@ RUNTIME_FUNCTION(Runtime_FunctionBindArguments) {
isolate->heap()->fixed_cow_array_map());
bound_function->set_function_bindings(*new_bindings);
// Update length.
// Update length. Have to remove the prototype first so that map migration
// is happy about the number of fields.
RUNTIME_ASSERT(bound_function->RemovePrototype());
Handle<Map> bound_function_map(
isolate->native_context()->bound_function_map());
JSObject::MigrateToMap(bound_function, bound_function_map);
Handle<String> length_string = isolate->factory()->length_string();
Handle<Object> new_length(args.at<Object>(3));
PropertyAttributes attr =
static_cast<PropertyAttributes>(DONT_DELETE | DONT_ENUM | READ_ONLY);
Runtime::ForceSetObjectProperty(
bound_function, length_string, new_length, attr).Assert();
JSObject::SetOwnPropertyIgnoreAttributes(bound_function, length_string,
new_length, attr);
return *bound_function;
}
......
......@@ -1781,19 +1781,15 @@ function FunctionBind(this_arg) { // Length is 1.
return %Apply(bindings[0], bindings[1], argv, 0, bound_argc + argc);
};
%FunctionRemovePrototype(boundFunction);
var new_length = 0;
if (%_ClassOf(this) == "Function") {
// Function or FunctionProxy.
var old_length = this.length;
// FunctionProxies might provide a non-UInt32 value. If so, ignore it.
if ((typeof old_length === "number") &&
((old_length >>> 0) === old_length)) {
var argc = %_ArgumentsLength();
if (argc > 0) argc--; // Don't count the thisArg as parameter.
new_length = old_length - argc;
if (new_length < 0) new_length = 0;
}
var old_length = this.length;
// FunctionProxies might provide a non-UInt32 value. If so, ignore it.
if ((typeof old_length === "number") &&
((old_length >>> 0) === old_length)) {
var argc = %_ArgumentsLength();
if (argc > 0) argc--; // Don't count the thisArg as parameter.
new_length = old_length - argc;
if (new_length < 0) new_length = 0;
}
// This runtime function finds any remaining arguments on the stack,
// so we don't pass the arguments object.
......
// Copyright 2014 the V8 project authors. All rights reserved.
// AUTO-GENERATED BY tools/generate-runtime-tests.py, DO NOT MODIFY
// Flags: --allow-natives-syntax --harmony
var _bound_function = function() {};
var _bindee = new Object();
var arg2 = undefined;
var _new_length = 1.5;
%FunctionBindArguments(_bound_function, _bindee, arg2, _new_length);
......@@ -48,9 +48,9 @@ EXPAND_MACROS = [
# remove or change runtime functions, but make sure we don't lose our ability
# to parse them!
EXPECTED_FUNCTION_COUNT = 358
EXPECTED_FUZZABLE_COUNT = 325
EXPECTED_FUZZABLE_COUNT = 326
EXPECTED_CCTEST_COUNT = 6
EXPECTED_UNKNOWN_COUNT = 5
EXPECTED_UNKNOWN_COUNT = 4
EXPECTED_BUILTINS_COUNT = 798
......@@ -143,6 +143,7 @@ CUSTOM_KNOWN_GOOD_INPUT = {
"DateParseString": [None, "new Array(8)", None],
"DefineOrRedefineAccessorProperty": [None, None, "function() {}",
"function() {}", 2, None],
"FunctionBindArguments": [None, None, "undefined", None, None],
"GetBreakLocations": [None, 0, None],
"GetDefaultReceiver": ["function() {}", None],
"GetImplFromInitializedIntlObject": ["new Intl.NumberFormat('en-US')", None],
......
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