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
+ };
Handle<DescriptorArray> ComputeFunctionInstanceDescriptor(
- bool make_prototype_read_only,
- bool make_prototype_enumerable = false);
+ PrototypePropertyMode prototypeMode);
void MakeFunctionInstancePrototypeWritable();
static bool CompileBuiltin(int index);
bool is_ecma_native) {
Handle<String> symbol = Factory::LookupAsciiSymbol(name);
Handle<Code> call_code = Handle<Code>(Builtins::builtin(call));
- Handle<JSFunction> function =
+ Handle<JSFunction> function = prototype.is_null() ?
+ Factory::NewFunctionWithoutPrototype(symbol, call_code) :
Factory::NewFunctionWithPrototype(symbol,
type,
instance_size,
Handle<DescriptorArray> Genesis::ComputeFunctionInstanceDescriptor(
- bool make_prototype_read_only,
- bool make_prototype_enumerable) {
+ PrototypePropertyMode prototypeMode) {
Handle<DescriptorArray> result = Factory::empty_descriptor_array();
- // Add prototype.
- PropertyAttributes attributes = static_cast<PropertyAttributes>(
- (make_prototype_enumerable ? 0 : DONT_ENUM)
- | DONT_DELETE
- | (make_prototype_read_only ? READ_ONLY : 0));
- result =
- Factory::CopyAppendProxyDescriptor(
- result,
- Factory::prototype_symbol(),
- Factory::NewProxy(&Accessors::FunctionPrototype),
- attributes);
+ if (prototypeMode != DONT_ADD_PROTOTYPE) {
+ PropertyAttributes attributes = static_cast<PropertyAttributes>(
+ DONT_ENUM |
+ DONT_DELETE |
+ (prototypeMode == ADD_READONLY_PROTOTYPE ? READ_ONLY : 0));
+ result =
+ Factory::CopyAppendProxyDescriptor(
+ result,
+ Factory::prototype_symbol(),
+ Factory::NewProxy(&Accessors::FunctionPrototype),
+ attributes);
+ }
- attributes =
+ PropertyAttributes attributes =
static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE | READ_ONLY);
// Add length.
result =
// Please note that the prototype property for function instances must be
// writable.
Handle<DescriptorArray> function_map_descriptors =
- ComputeFunctionInstanceDescriptor(false, false);
+ ComputeFunctionInstanceDescriptor(ADD_WRITEABLE_PROTOTYPE);
fm->set_instance_descriptors(*function_map_descriptors);
+ fm->set_function_with_prototype(true);
+
+ // Functions with this map will not have a 'prototype' property, and
+ // can not be used as constructors.
+ fm = Factory::NewMap(JS_FUNCTION_TYPE, JSFunction::kSize);
+ global_context()->set_function_without_prototype_map(*fm);
+ function_map_descriptors =
+ ComputeFunctionInstanceDescriptor(DONT_ADD_PROTOTYPE);
+ fm->set_instance_descriptors(*function_map_descriptors);
+ fm->set_function_with_prototype(false);
// Allocate the function map first and then patch the prototype later
fm = Factory::NewMap(JS_FUNCTION_TYPE, JSFunction::kSize);
global_context()->set_function_map(*fm);
- function_map_descriptors = ComputeFunctionInstanceDescriptor(true);
+ function_map_descriptors =
+ ComputeFunctionInstanceDescriptor(ADD_READONLY_PROTOTYPE);
fm->set_instance_descriptors(*function_map_descriptors);
+ fm->set_function_with_prototype(true);
Handle<String> object_name = Handle<String>(Heap::Object_symbol());
global_context()->function_map()->set_prototype(*empty_function);
global_context()->function_instance_map()->set_prototype(*empty_function);
+ // Allocate a distinct prototype for the function map for functions without
+ // prototype, so it will not add 'prototype' property in the proto chain.
+ global_context()->function_without_prototype_map()->set_prototype(
+ *Factory::NewJSObject(Top::object_function(), TENURED));
+
// Allocate the function map first and then patch the prototype later
Handle<Map> empty_fm = Factory::CopyMapDropDescriptors(fm);
empty_fm->set_instance_descriptors(*function_map_descriptors);
// Install the call and the apply functions.
Handle<JSFunction> call =
InstallFunction(proto, "call", JS_OBJECT_TYPE, JSObject::kHeaderSize,
- Factory::NewJSObject(Top::object_function(), TENURED),
+ Handle<JSObject>::null(),
Builtins::FunctionCall,
false);
Handle<JSFunction> apply =
InstallFunction(proto, "apply", JS_OBJECT_TYPE, JSObject::kHeaderSize,
- Factory::NewJSObject(Top::object_function(), TENURED),
+ Handle<JSObject>::null(),
Builtins::FunctionApply,
false);
// Set the lengths for the functions to satisfy ECMA-262.
call->shared()->set_length(1);
apply->shared()->set_length(2);
+
+ // Install the call, apply, toString and constructor properties
+ // for the functions without prototype.
+ Handle<JSObject> wp_proto = Handle<JSObject>(
+ JSObject::cast(Top::function_without_prototype_map()->prototype()));
+
+ Handle<String> call_symbol = Factory::LookupAsciiSymbol("call");
+ SetProperty(wp_proto, call_symbol, call, DONT_ENUM);
+
+ Handle<String> apply_symbol = Factory::LookupAsciiSymbol("apply");
+ SetProperty(wp_proto, apply_symbol, apply, DONT_ENUM);
+
+ Handle<Object> to_string = GetProperty(proto, "toString");
+ Handle<String> to_string_symbol = Factory::LookupAsciiSymbol("toString");
+ SetProperty(wp_proto, to_string_symbol, to_string, DONT_ENUM);
+
+ SetProperty(wp_proto, Factory::constructor_symbol(), function, DONT_ENUM);
}
// Create a constructor for RegExp results (a variant of Array that
HandleScope scope;
Handle<DescriptorArray> function_map_descriptors =
- ComputeFunctionInstanceDescriptor(false);
+ ComputeFunctionInstanceDescriptor(ADD_WRITEABLE_PROTOTYPE);
Handle<Map> fm = Factory::CopyMapDropDescriptors(Top::function_map());
fm->set_instance_descriptors(*function_map_descriptors);
+ fm->set_function_with_prototype(true);
Top::context()->global_context()->set_function_map(*fm);
}
V(INSTANTIATE_FUN_INDEX, JSFunction, instantiate_fun) \
V(CONFIGURE_INSTANCE_FUN_INDEX, JSFunction, configure_instance_fun) \
V(FUNCTION_MAP_INDEX, Map, function_map) \
+ V(FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX, Map, function_without_prototype_map) \
V(FUNCTION_INSTANCE_MAP_INDEX, Map, function_instance_map) \
V(JS_ARRAY_MAP_INDEX, Map, js_array_map)\
V(REGEXP_RESULT_MAP_INDEX, Map, regexp_result_map)\
JS_ARRAY_MAP_INDEX,
REGEXP_RESULT_MAP_INDEX,
FUNCTION_MAP_INDEX,
+ FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX,
FUNCTION_INSTANCE_MAP_INDEX,
INITIAL_OBJECT_PROTOTYPE_INDEX,
BOOLEAN_FUNCTION_INDEX,
}
+Handle<JSFunction> Factory::NewFunctionWithoutPrototype(Handle<String> name,
+ Handle<Code> code) {
+ Handle<JSFunction> function = NewFunctionWithoutPrototype(name);
+ function->set_code(*code);
+ ASSERT(!function->has_initial_map());
+ ASSERT(!function->has_prototype());
+ return function;
+}
+
+
Handle<Code> Factory::NewCode(const CodeDesc& desc,
ZoneScopeInfo* sinfo,
Code::Flags flags,
}
+Handle<JSFunction> Factory::NewFunctionWithoutPrototypeHelper(
+ Handle<String> name) {
+ Handle<SharedFunctionInfo> function_share = NewSharedFunctionInfo(name);
+ CALL_HEAP_FUNCTION(Heap::AllocateFunction(
+ *Top::function_without_prototype_map(),
+ *function_share,
+ *the_hole_value()),
+ JSFunction);
+}
+
+
+Handle<JSFunction> Factory::NewFunctionWithoutPrototype(Handle<String> name) {
+ Handle<JSFunction> fun = NewFunctionWithoutPrototypeHelper(name);
+ fun->set_context(Top::context()->global_context());
+ return fun;
+}
+
+
Handle<Object> Factory::ToObject(Handle<Object> object) {
CALL_HEAP_FUNCTION(object->ToObject(), Object);
}
static Handle<JSFunction> NewFunction(Handle<String> name,
Handle<Object> prototype);
+ static Handle<JSFunction> NewFunctionWithoutPrototype(Handle<String> name);
+
static Handle<JSFunction> NewFunction(Handle<Object> super, bool is_global);
static Handle<JSFunction> BaseNewFunctionFromSharedFunctionInfo(
Handle<Code> code,
bool force_initial_map);
+ static Handle<JSFunction> NewFunctionWithoutPrototype(Handle<String> name,
+ Handle<Code> code);
+
static Handle<DescriptorArray> CopyAppendProxyDescriptor(
Handle<DescriptorArray> array,
Handle<String> key,
static Handle<JSFunction> NewFunctionHelper(Handle<String> name,
Handle<Object> prototype);
+ static Handle<JSFunction> NewFunctionWithoutPrototypeHelper(
+ Handle<String> name);
+
static Handle<DescriptorArray> CopyAppendCallbackDescriptors(
Handle<DescriptorArray> array,
Handle<Object> descriptors);
Handle<Object> SetPrototype(Handle<JSFunction> function,
Handle<Object> prototype) {
+ ASSERT(function->should_have_prototype());
CALL_HEAP_FUNCTION(Accessors::FunctionSetPrototype(*function,
*prototype,
NULL),
}
// Use specialized code for getting prototype of functions.
- if (object->IsJSFunction() && name->Equals(Heap::prototype_symbol())) {
+ if (object->IsJSFunction() && name->Equals(Heap::prototype_symbol()) &&
+ JSFunction::cast(*object)->should_have_prototype()) {
#ifdef DEBUG
if (FLAG_trace_ic) PrintF("[LoadIC : +#prototype /function]\n");
#endif
}
// Use specialized code for getting prototype of functions.
- if (object->IsJSFunction() && name->Equals(Heap::prototype_symbol())) {
+ if (object->IsJSFunction() && name->Equals(Heap::prototype_symbol()) &&
+ JSFunction::cast(*object)->should_have_prototype()) {
Handle<JSFunction> function = Handle<JSFunction>::cast(object);
Object* code =
StubCache::ComputeKeyedLoadFunctionPrototype(*name, *function);
}
+void Map::set_function_with_prototype(bool value) {
+ if (value) {
+ set_bit_field2(bit_field2() | (1 << kFunctionWithPrototype));
+ } else {
+ set_bit_field2(bit_field2() & ~(1 << kFunctionWithPrototype));
+ }
+}
+
+
+bool Map::function_with_prototype() {
+ return ((1 << kFunctionWithPrototype) & bit_field2()) != 0;
+}
+
+
void Map::set_is_access_check_needed(bool access_check_needed) {
if (access_check_needed) {
set_bit_field(bit_field() | (1 << kIsAccessCheckNeeded));
return instance_prototype();
}
+bool JSFunction::should_have_prototype() {
+ return map()->function_with_prototype();
+}
+
bool JSFunction::is_compiled() {
return shared()->is_compiled();
Object* JSFunction::SetPrototype(Object* value) {
+ ASSERT(should_have_prototype());
Object* construct_prototype = value;
// If the value is not a JSObject, store the value in the map's
}
+Object* JSFunction::RemovePrototype() {
+ ASSERT(map() == context()->global_context()->function_map());
+ set_map(context()->global_context()->function_without_prototype_map());
+ set_prototype_or_initial_map(Heap::the_hole_value());
+ return this;
+}
+
+
Object* JSFunction::SetInstanceClassName(String* name) {
shared()->set_instance_class_name(name);
return this;
inline void set_non_instance_prototype(bool value);
inline bool has_non_instance_prototype();
+ // Tells whether function has special prototype property. If not, prototype
+ // property will not be created when accessed (will return undefined),
+ // and construction from this function will not be allowed.
+ inline void set_function_with_prototype(bool value);
+ inline bool function_with_prototype();
+
// Tells whether the instance with this map should be ignored by the
// __proto__ accessor.
inline void set_is_hidden_prototype() {
// Bit positions for bit field 2
static const int kIsExtensible = 0;
+ static const int kFunctionWithPrototype = 1;
// Layout of the default cache. It holds alternating name and code objects.
static const int kCodeCacheEntrySize = 2;
Object* SetInstancePrototype(Object* value);
Object* SetPrototype(Object* value);
+ // After prototype is removed, it will not be created when accessed, and
+ // [[Construct]] from this function will not be allowed.
+ Object* RemovePrototype();
+ inline bool should_have_prototype();
+
// Accessor for this function's initial map's [[class]]
// property. This is primarily used by ECMA native functions. This
// method sets the class_name field of this function's initial map
}
+static Object* Runtime_FunctionRemovePrototype(Arguments args) {
+ NoHandleAllocation ha;
+ ASSERT(args.length() == 1);
+
+ CONVERT_CHECKED(JSFunction, f, args[0]);
+ Object* obj = f->RemovePrototype();
+ if (obj->IsFailure()) return obj;
+
+ return Heap::undefined_value();
+}
+
+
static Object* Runtime_FunctionGetScript(Arguments args) {
HandleScope scope;
ASSERT(args.length() == 1);
ASSERT(args.length() == 2);
CONVERT_CHECKED(JSFunction, fun, args[0]);
+ ASSERT(fun->should_have_prototype());
Object* obj = Accessors::FunctionSetPrototype(fun, args[1], NULL);
if (obj->IsFailure()) return obj;
return args[0]; // return TOS
}
Handle<JSFunction> function = Handle<JSFunction>::cast(constructor);
+
+ // If function should not have prototype, construction is not allowed. In this
+ // case generated code bailouts here, since function has no initial_map.
+ if (!function->should_have_prototype()) {
+ Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
+ Handle<Object> type_error =
+ Factory::NewTypeError("not_constructor", arguments);
+ return Top::Throw(*type_error);
+ }
+
#ifdef ENABLE_DEBUGGER_SUPPORT
// Handle stepping into constructors if step into is active.
if (Debug::StepInActive()) {
F(FunctionSetPrototype, 2, 1) \
F(FunctionGetName, 1, 1) \
F(FunctionSetName, 2, 1) \
+ F(FunctionRemovePrototype, 1, 1) \
F(FunctionGetSourceCode, 1, 1) \
F(FunctionGetScript, 1, 1) \
F(FunctionGetScriptSourcePosition, 1, 1) \
var key = functions[i];
var f = functions[i + 1];
%FunctionSetName(f, key);
+ %FunctionRemovePrototype(f);
%SetProperty(object, key, f, attributes);
}
%ToFastProperties(object);
--- /dev/null
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Tests that function does not have prototype.
+function testPrototype(f) {
+ assertFalse('prototype' in f);
+ assertEquals(undefined, f.prototype);
+ f.prototype = 42;
+ assertEquals(42, f.prototype);
+ assertTrue('prototype' in f);
+}
+
+// Tests that construction from function throws.
+function testConstruction(name) {
+ assertThrows("new " + name + "()");
+ eval(name + ".prototype = 42;");
+ assertThrows("new " + name + "()");
+}
+
+testPrototype(eval);
+testPrototype(Array.prototype.push);
+testPrototype(Function.prototype.call);
+testPrototype(String.fromCharCode);
+var date = new Date();
+testPrototype(date.toString);
+
+testConstruction("parseInt");
+testConstruction("Function.prototype.apply");
+var regexp = /abc/g;
+testConstruction("regexp.test");
S15.10.7_A1_T1: FAIL_OK
S15.10.7_A1_T2: FAIL_OK
-# We allow construct calls to built-in functions, and we allow built-in
-# functions to have prototypes.
-S15.1.2.1_A4.6: FAIL_OK
-S15.1.2.1_A4.7: FAIL_OK
-S15.1.2.2_A9.6: FAIL_OK
-S15.1.2.2_A9.7: FAIL_OK
-S15.1.2.3_A7.6: FAIL_OK
-S15.1.2.3_A7.7: FAIL_OK
-S15.1.2.4_A2.6: FAIL_OK
-S15.1.2.4_A2.7: FAIL_OK
-S15.1.2.5_A2.6: FAIL_OK
-S15.1.2.5_A2.7: FAIL_OK
-S15.1.3.1_A5.6: FAIL_OK
-S15.1.3.1_A5.7: FAIL_OK
-S15.1.3.2_A5.6: FAIL_OK
-S15.1.3.2_A5.7: FAIL_OK
-S15.1.3.3_A5.6: FAIL_OK
-S15.1.3.3_A5.7: FAIL_OK
-S15.1.3.4_A5.6: FAIL_OK
-S15.1.3.4_A5.7: FAIL_OK
-S15.10.6.2_A6: FAIL_OK
-S15.10.6.3_A6: FAIL_OK
-S15.10.6.4_A6: FAIL_OK
-S15.10.6.4_A7: FAIL_OK
-S15.2.4.2_A6: FAIL_OK
-S15.2.4.3_A6: FAIL_OK
-S15.2.4.4_A6: FAIL_OK
-S15.2.4.5_A6: FAIL_OK
-S15.2.4.6_A6: FAIL_OK
-S15.2.4.7_A6: FAIL_OK
-S15.3.4.2_A6: FAIL_OK
-S15.4.4.10_A5.6: FAIL_OK
-S15.4.4.10_A5.7: FAIL_OK
-S15.4.4.11_A7.6: FAIL_OK
-S15.4.4.11_A7.7: FAIL_OK
-S15.4.4.12_A5.6: FAIL_OK
-S15.4.4.12_A5.7: FAIL_OK
-S15.4.4.13_A5.6: FAIL_OK
-S15.4.4.13_A5.7: FAIL_OK
-S15.4.4.2_A4.6: FAIL_OK
-S15.4.4.3_A4.6: FAIL_OK
-S15.4.4.3_A4.6: FAIL_OK
-S15.4.4.4_A4.6: FAIL_OK
-S15.4.4.4_A4.7: FAIL_OK
-S15.4.4.5_A6.6: FAIL_OK
-S15.4.4.5_A6.7: FAIL_OK
-S15.4.4.6_A5.6: FAIL_OK
-S15.4.4.6_A5.7: FAIL_OK
-S15.4.4.7_A6.6: FAIL_OK
-S15.4.4.7_A6.7: FAIL_OK
-S15.4.4.8_A5.6: FAIL_OK
-S15.4.4.8_A5.7: FAIL_OK
-S15.4.4.9_A5.6: FAIL_OK
-S15.4.4.9_A5.7: FAIL_OK
-S15.5.4.10_A6: FAIL_OK
-S15.5.4.11_A6: FAIL_OK
-S15.5.4.12_A6: FAIL_OK
-S15.5.4.13_A6: FAIL_OK
-S15.5.4.14_A6: FAIL_OK
-S15.5.4.15_A6: FAIL_OK
-S15.5.4.16_A6: FAIL_OK
-S15.5.4.17_A6: FAIL_OK
-S15.5.4.18_A6: FAIL_OK
-S15.5.4.19_A6: FAIL_OK
-S15.5.4.4_A6: FAIL_OK
-S15.5.4.5_A6: FAIL_OK
-S15.5.4.6_A6: FAIL_OK
-S15.5.4.7_A6: FAIL_OK
-S15.5.4.9_A6: FAIL_OK
-S15.3.4.3_A12: FAIL_OK
-S15.3.4.4_A12: FAIL_OK
-S15.5.4.8_A6: FAIL_OK
-
# We are silent in some regexp cases where the spec wants us to give
# errors, for compatibility.
S15.10.2.11_A1_T2: FAIL