Implement Function.prototype.toMethod.

R=arv@chromium.org, verwaest@chromium.org
BUG=v8:3330
LOG=N

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

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@23274 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
dslomov@chromium.org 2014-08-21 12:39:33 +00:00
parent e6d32405be
commit 22d5ceb1f2
16 changed files with 378 additions and 162 deletions

View File

@ -242,6 +242,7 @@ action("js2c_experimental") {
"src/generator.js",
"src/harmony-string.js",
"src/harmony-array.js",
"src/harmony-classes.js",
]
outputs = [

View File

@ -5669,7 +5669,7 @@ class Internals {
static const int kNullValueRootIndex = 7;
static const int kTrueValueRootIndex = 8;
static const int kFalseValueRootIndex = 9;
static const int kEmptyStringRootIndex = 161;
static const int kEmptyStringRootIndex = 162;
// The external allocation limit should be below 256 MB on all architectures
// to avoid that resource-constrained embedders run low on memory.

View File

@ -2063,6 +2063,7 @@ bool Genesis::InstallExperimentalNatives() {
INSTALL_EXPERIMENTAL_NATIVE(i, generators, "generator.js")
INSTALL_EXPERIMENTAL_NATIVE(i, strings, "harmony-string.js")
INSTALL_EXPERIMENTAL_NATIVE(i, arrays, "harmony-array.js")
INSTALL_EXPERIMENTAL_NATIVE(i, classes, "harmony-classes.js")
}
InstallExperimentalNativeFunctions();

32
src/harmony-classes.js Normal file
View File

@ -0,0 +1,32 @@
// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// This file relies on the fact that the following declarations have been made
// in runtime.js:
// var $Function = global.Function;
// var $Array = global.Array;
(function() {
function FunctionToMethod(homeObject) {
if (!IS_SPEC_FUNCTION(this)) {
throw MakeTypeError('toMethod_non_function',
[%ToString(this), typeof this]);
}
if (!IS_SPEC_OBJECT(homeObject)) {
throw MakeTypeError('toMethod_non_object',
[%ToString(homeObject)]);
}
return %ToMethod(this, homeObject);
}
%CheckIsBootstrapping();
InstallFunctions($Function.prototype, DONT_ENUM, $Array(
"toMethod", FunctionToMethod
));
}());

View File

@ -2878,6 +2878,7 @@ void Heap::CreateInitialObjects() {
set_observed_symbol(*factory->NewPrivateSymbol());
set_stack_trace_symbol(*factory->NewPrivateSymbol());
set_uninitialized_symbol(*factory->NewPrivateSymbol());
set_home_object_symbol(*factory->NewPrivateOwnSymbol());
Handle<SeededNumberDictionary> slow_element_dictionary =
SeededNumberDictionary::New(isolate(), 0, TENURED);

View File

@ -185,6 +185,7 @@ namespace internal {
V(Symbol, stack_trace_symbol, StackTraceSymbol) \
V(Symbol, detailed_stack_trace_symbol, DetailedStackTraceSymbol) \
V(Symbol, normal_ic_symbol, NormalICSymbol) \
V(Symbol, home_object_symbol, HomeObjectSymbol) \
V(FixedArray, materialized_objects, MaterializedObjects) \
V(FixedArray, allocation_sites_scratchpad, AllocationSitesScratchpad) \
V(FixedArray, microtask_queue, MicrotaskQueue)

View File

@ -44,6 +44,8 @@ var kMessages = {
no_setter_in_callback: ["Cannot set property ", "%0", " of ", "%1", " which has only a getter"],
apply_non_function: ["Function.prototype.apply was called on ", "%0", ", which is a ", "%1", " and not a function"],
apply_wrong_args: ["Function.prototype.apply: Arguments list has wrong type"],
toMethod_non_function: ["Function.prototype.toMethod was called on ", "%0", ", which is a ", "%1", " and not a function"],
toMethod_non_object: ["Function.prototype.toMethod: home object ", "%0", " is not an object"],
invalid_in_operator_use: ["Cannot use 'in' operator to search for '", "%0", "' in ", "%1"],
instanceof_function_expected: ["Expecting a function in instanceof check, but got ", "%0"],
instanceof_nonobject_proto: ["Function has non-object prototype '", "%0", "' in instanceof check"],

View File

@ -9284,6 +9284,30 @@ void JSFunction::MarkInOptimizationQueue() {
}
Handle<JSFunction> JSFunction::CloneClosure(Handle<JSFunction> function) {
Isolate* isolate = function->GetIsolate();
Handle<Map> map(function->map());
Handle<SharedFunctionInfo> shared(function->shared());
Handle<Context> context(function->context());
Handle<JSFunction> clone =
isolate->factory()->NewFunctionFromSharedFunctionInfo(shared, context);
if (shared->bound()) {
clone->set_function_bindings(function->function_bindings());
}
// In typical case, __proto__ of ``function`` is the default Function
// prototype, which means that SetPrototype below is a no-op.
// In rare cases when that is not true, we mutate the clone's __proto__.
Handle<Object> original_prototype(map->prototype(), isolate);
if (*original_prototype != clone->map()->prototype()) {
JSObject::SetPrototype(clone, original_prototype, false).Assert();
}
return clone;
}
void SharedFunctionInfo::AddToOptimizedCodeMap(
Handle<SharedFunctionInfo> shared,
Handle<Context> native_context,

View File

@ -7711,6 +7711,11 @@ class JSFunction: public JSObject {
static void SetInstancePrototype(Handle<JSFunction> function,
Handle<Object> value);
// Creates a new closure for the fucntion with the same bindings,
// bound values, and prototype. An equivalent of spec operations
// ``CloneMethod`` and ``CloneBoundFunction``.
static Handle<JSFunction> CloneClosure(Handle<JSFunction> function);
// After prototype is removed, it will not be created when accessed, and
// [[Construct]] from this function will not be allowed.
bool RemovePrototype();

View File

@ -2051,6 +2051,25 @@ RUNTIME_FUNCTION(Runtime_PreventExtensions) {
}
RUNTIME_FUNCTION(Runtime_ToMethod) {
HandleScope scope(isolate);
DCHECK(args.length() == 2);
CONVERT_ARG_HANDLE_CHECKED(JSFunction, fun, 0);
CONVERT_ARG_HANDLE_CHECKED(JSObject, home_object, 1);
Handle<JSFunction> clone = JSFunction::CloneClosure(fun);
Handle<Symbol> home_object_symbol(isolate->heap()->home_object_symbol());
JSObject::SetOwnPropertyIgnoreAttributes(clone, home_object_symbol,
home_object, DONT_ENUM).Assert();
return *clone;
}
RUNTIME_FUNCTION(Runtime_HomeObjectSymbol) {
DCHECK(args.length() == 0);
return isolate->heap()->home_object_symbol();
}
RUNTIME_FUNCTION(Runtime_IsExtensible) {
SealHandleScope shs(isolate);
DCHECK(args.length() == 1);

View File

@ -179,7 +179,11 @@ namespace internal {
F(NumberToFixed, 2, 1) \
F(NumberToExponential, 2, 1) \
F(NumberToPrecision, 2, 1) \
F(IsValidSmi, 1, 1)
F(IsValidSmi, 1, 1) \
\
/* Classes support */ \
F(ToMethod, 2, 1) \
F(HomeObjectSymbol, 0, 1)
#define RUNTIME_FUNCTION_LIST_ALWAYS_2(F) \

View File

@ -0,0 +1,115 @@
// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --harmony-classes --allow-natives-syntax
(function TestSingleClass() {
function f(x) {
var a = [0, 1, 2]
return a[x];
}
function ClassD() { }
assertEquals(1, f(1));
var g = f.toMethod(ClassD.prototype);
assertEquals(1, g(1));
assertEquals(undefined, f[%HomeObjectSymbol()]);
assertEquals(ClassD.prototype, g[%HomeObjectSymbol()]);
}());
(function TestClassHierarchy() {
function f(x) {
return function g(y) { x++; return x + y; };
}
function Base() {}
function Derived() { }
Derived.prototype = Object.create(Base.prototype);
var q = f(0);
assertEquals(2, q(1));
assertEquals(3, q(1));
var g = q.toMethod(Derived.prototype);
assertFalse(g === q);
assertEquals(4, g(1));
assertEquals(5, q(1));
}());
(function TestErrorCases() {
var sFun = Function.prototype.toMethod;
assertThrows(function() { sFun.call({}); }, TypeError);
assertThrows(function() { sFun.call({}, {}); }, TypeError);
function f(){};
assertThrows(function() { f.toMethod(1); }, TypeError);
}());
(function TestPrototypeChain() {
var o = {};
var o1 = {};
function f() { }
function g() { }
var fMeth = f.toMethod(o);
assertEquals(o, fMeth[%HomeObjectSymbol()]);
g.__proto__ = fMeth;
assertEquals(undefined, g[%HomeObjectSymbol()]);
var gMeth = g.toMethod(o1);
assertEquals(fMeth, gMeth.__proto__);
assertEquals(o, fMeth[%HomeObjectSymbol()]);
assertEquals(o1, gMeth[%HomeObjectSymbol()]);
}());
(function TestBoundFunction() {
var o = {};
var p = {};
function f(x, y, z, w) {
assertEquals(o, this);
assertEquals(1, x);
assertEquals(2, y);
assertEquals(3, z);
assertEquals(4, w);
return x+y+z+w;
}
var fBound = f.bind(o, 1, 2, 3);
var fMeth = fBound.toMethod(p);
assertEquals(10, fMeth(4));
assertEquals(10, fMeth.call(p, 4));
var fBound1 = fBound.bind(o, 4);
assertEquals(10, fBound1());
var fMethBound = fMeth.bind(o, 4);
assertEquals(10, fMethBound());
}());
(function TestOptimized() {
function f(o) {
return o.x;
}
var o = {x : 15};
assertEquals(15, f(o));
assertEquals(15, f(o));
%OptimizeFunctionOnNextCall(f);
assertEquals(15, f(o));
var g = f.toMethod({});
var o1 = {y : 1024, x : "abc"};
assertEquals("abc", f(o1));
assertEquals("abc", g(o1));
} ());
(function TestExtensibility() {
function f() {}
Object.preventExtensions(f);
assertFalse(Object.isExtensible(f));
var m = f.toMethod({});
assertTrue(Object.isExtensible(m));
}());

View File

@ -0,0 +1,4 @@
// 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 --harmony-proxies
%HomeObjectSymbol();

View File

@ -0,0 +1,6 @@
// 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 --harmony-proxies
var _fun = function() {};
var _home_object = new Object();
%ToMethod(_fun, _home_object);

View File

@ -47,8 +47,8 @@ EXPAND_MACROS = [
# that the parser doesn't bit-rot. Change the values as needed when you add,
# remove or change runtime functions, but make sure we don't lose our ability
# to parse them!
EXPECTED_FUNCTION_COUNT = 429
EXPECTED_FUZZABLE_COUNT = 330
EXPECTED_FUNCTION_COUNT = 431
EXPECTED_FUZZABLE_COUNT = 332
EXPECTED_CCTEST_COUNT = 7
EXPECTED_UNKNOWN_COUNT = 17
EXPECTED_BUILTINS_COUNT = 808

View File

@ -1425,6 +1425,7 @@
'../../src/generator.js',
'../../src/harmony-string.js',
'../../src/harmony-array.js',
'../../src/harmony-classes.js',
],
'libraries_bin_file': '<(SHARED_INTERMEDIATE_DIR)/libraries.bin',
'libraries_experimental_bin_file': '<(SHARED_INTERMEDIATE_DIR)/libraries-experimental.bin',