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:
parent
e6d32405be
commit
22d5ceb1f2
1
BUILD.gn
1
BUILD.gn
@ -242,6 +242,7 @@ action("js2c_experimental") {
|
||||
"src/generator.js",
|
||||
"src/harmony-string.js",
|
||||
"src/harmony-array.js",
|
||||
"src/harmony-classes.js",
|
||||
]
|
||||
|
||||
outputs = [
|
||||
|
@ -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.
|
||||
|
@ -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
32
src/harmony-classes.js
Normal 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
|
||||
));
|
||||
}());
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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"],
|
||||
|
@ -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,
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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) \
|
||||
|
115
test/mjsunit/harmony/toMethod.js
Normal file
115
test/mjsunit/harmony/toMethod.js
Normal 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));
|
||||
}());
|
4
test/mjsunit/runtime-gen/homeobjectsymbol.js
Normal file
4
test/mjsunit/runtime-gen/homeobjectsymbol.js
Normal 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();
|
6
test/mjsunit/runtime-gen/tomethod.js
Normal file
6
test/mjsunit/runtime-gen/tomethod.js
Normal 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);
|
@ -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
|
||||
|
@ -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',
|
||||
|
Loading…
Reference in New Issue
Block a user