Commit 80bd958d authored by mmaly@chromium.org's avatar mmaly@chromium.org

Strict mode ThrowTypeError functions for

- function.caller
- function.arguments

Review URL: http://codereview.chromium.org/6691003/

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@7168 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 8700f796
......@@ -3112,9 +3112,10 @@ void CodeGenerator::InstantiateFunction(
bool pretenure) {
// Use the fast case closure allocation code that allocates in new
// space for nested functions that don't need literals cloning.
if (scope()->is_function_scope() &&
if (!pretenure &&
scope()->is_function_scope() &&
function_info->num_literals() == 0 &&
!pretenure) {
!function_info->strict_mode()) { // Strict mode functions use slow path.
FastNewClosureStub stub;
frame_->EmitPush(Operand(function_info));
frame_->SpillAll();
......
......@@ -1084,9 +1084,10 @@ void FullCodeGenerator::EmitNewClosure(Handle<SharedFunctionInfo> info,
// doesn't just get a copy of the existing unoptimized code.
if (!FLAG_always_opt &&
!FLAG_prepare_always_opt &&
!pretenure &&
scope()->is_function_scope() &&
info->num_literals() == 0 &&
!pretenure) {
!info->strict_mode()) { // Strict mode functions use slow path.
FastNewClosureStub stub;
__ mov(r0, Operand(info));
__ push(r0);
......
......@@ -3717,7 +3717,8 @@ void LCodeGen::DoFunctionLiteral(LFunctionLiteral* instr) {
// space for nested functions that don't need literals cloning.
Handle<SharedFunctionInfo> shared_info = instr->shared_info();
bool pretenure = instr->hydrogen()->pretenure();
if (shared_info->num_literals() == 0 && !pretenure) {
if (!pretenure && shared_info->num_literals() == 0 &&
!shared_info->strict_mode()) { // Strict mode functions use slow path.
FastNewClosureStub stub;
__ mov(r1, Operand(shared_info));
__ push(r1);
......
......@@ -207,6 +207,10 @@ class Genesis BASE_EMBEDDED {
void CreateRoots();
// Creates the empty function. Used for creating a context from scratch.
Handle<JSFunction> CreateEmptyFunction();
void CreateThrowTypeErrorCallbacks(
Handle<FixedArray> callbacks,
Builtins::Name builtin);
void CreateThrowTypeError(Handle<JSFunction> empty);
// Creates the global objects using the global and the template passed in
// through the API. We call this regardless of whether we are building a
// context from scratch or using a deserialized one from the partial snapshot
......@@ -263,6 +267,10 @@ class Genesis BASE_EMBEDDED {
Handle<DescriptorArray> ComputeFunctionInstanceDescriptor(
PrototypePropertyMode prototypeMode);
void MakeFunctionInstancePrototypeWritable();
Handle<DescriptorArray> ComputeStrictFunctionDescriptor(
PrototypePropertyMode propertyMode,
Handle<FixedArray> arguments,
Handle<FixedArray> caller);
static bool CompileBuiltin(int index);
static bool CompileNative(Vector<const char> name, Handle<String> source);
......@@ -499,6 +507,113 @@ Handle<JSFunction> Genesis::CreateEmptyFunction() {
}
Handle<DescriptorArray> Genesis::ComputeStrictFunctionDescriptor(
PrototypePropertyMode prototypeMode,
Handle<FixedArray> arguments,
Handle<FixedArray> caller) {
Handle<DescriptorArray> descriptors =
Factory::NewDescriptorArray(prototypeMode == DONT_ADD_PROTOTYPE ? 4 : 5);
PropertyAttributes attributes = static_cast<PropertyAttributes>(
DONT_ENUM | DONT_DELETE | READ_ONLY);
{ // length
Handle<Proxy> proxy = Factory::NewProxy(&Accessors::FunctionLength);
CallbacksDescriptor d(*Factory::length_symbol(), *proxy, attributes);
descriptors->Set(0, &d);
}
{ // name
Handle<Proxy> proxy = Factory::NewProxy(&Accessors::FunctionName);
CallbacksDescriptor d(*Factory::name_symbol(), *proxy, attributes);
descriptors->Set(1, &d);
}
{ // arguments
CallbacksDescriptor d(*Factory::arguments_symbol(), *arguments, attributes);
descriptors->Set(2, &d);
}
{ // caller
CallbacksDescriptor d(*Factory::caller_symbol(), *caller, attributes);
descriptors->Set(3, &d);
}
// prototype
if (prototypeMode != DONT_ADD_PROTOTYPE) {
if (prototypeMode == ADD_WRITEABLE_PROTOTYPE) {
attributes = static_cast<PropertyAttributes>(attributes & ~READ_ONLY);
}
CallbacksDescriptor d(
*Factory::prototype_symbol(),
*Factory::NewProxy(&Accessors::FunctionPrototype),
attributes);
descriptors->Set(4, &d);
}
descriptors->Sort();
return descriptors;
}
void Genesis::CreateThrowTypeErrorCallbacks(
Handle<FixedArray> callbacks,
Builtins::Name builtin) {
// Create the ThrowTypeError function.
Handle<String> name = Factory::LookupAsciiSymbol("ThrowTypeError");
Handle<JSFunction> pill = Factory::NewFunctionWithoutPrototypeStrict(name);
Handle<Code> code = Handle<Code>(Builtins::builtin(builtin));
pill->set_map(global_context()->function_map_strict());
pill->set_code(*code);
pill->shared()->set_code(*code);
pill->shared()->DontAdaptArguments();
// Install the poison pills into the calbacks array.
callbacks->set(0, *pill);
callbacks->set(1, *pill);
PreventExtensions(pill);
}
// ECMAScript 5th Edition, 13.2.3
void Genesis::CreateThrowTypeError(Handle<JSFunction> empty) {
// Create the pill callbacks arrays. The get/set callacks are installed
// after the maps get created below.
Handle<FixedArray> arguments = Factory::NewFixedArray(2, TENURED);
Handle<FixedArray> caller = Factory::NewFixedArray(2, TENURED);
{ // Allocate map for the strict mode function instances.
Handle<Map> map = Factory::NewMap(JS_FUNCTION_TYPE, JSFunction::kSize);
global_context()->set_function_instance_map_strict(*map);
Handle<DescriptorArray> descriptors = ComputeStrictFunctionDescriptor(
ADD_WRITEABLE_PROTOTYPE, arguments, caller);
map->set_instance_descriptors(*descriptors);
map->set_function_with_prototype(true);
map->set_prototype(*empty);
}
{ // Allocate map for the prototype-less strict mode instances.
Handle<Map> map = Factory::NewMap(JS_FUNCTION_TYPE, JSFunction::kSize);
global_context()->set_function_without_prototype_map_strict(*map);
Handle<DescriptorArray> descriptors = ComputeStrictFunctionDescriptor(
DONT_ADD_PROTOTYPE, arguments, caller);
map->set_instance_descriptors(*descriptors);
map->set_function_with_prototype(false);
map->set_prototype(*empty);
}
{ // Allocate map for the strict mode functions.
Handle<Map> map = Factory::NewMap(JS_FUNCTION_TYPE, JSFunction::kSize);
global_context()->set_function_map_strict(*map);
Handle<DescriptorArray> descriptors = ComputeStrictFunctionDescriptor(
ADD_READONLY_PROTOTYPE, arguments, caller);
map->set_instance_descriptors(*descriptors);
map->set_function_with_prototype(true);
map->set_prototype(*empty);
}
CreateThrowTypeErrorCallbacks(arguments, Builtins::StrictFunctionArguments);
CreateThrowTypeErrorCallbacks(caller, Builtins::StrictFunctionCaller);
}
static void AddToWeakGlobalContextList(Context* context) {
ASSERT(context->IsGlobalContext());
#ifdef DEBUG
......@@ -1858,6 +1973,7 @@ Genesis::Genesis(Handle<Object> global_object,
// We get here if there was no context snapshot.
CreateRoots();
Handle<JSFunction> empty_function = CreateEmptyFunction();
CreateThrowTypeError(empty_function);
Handle<GlobalObject> inner_global;
Handle<JSGlobalProxy> global_proxy =
CreateNewGlobals(global_template, global_object, &inner_global);
......
......@@ -958,6 +958,24 @@ BUILTIN(ArrayConcat) {
}
// -----------------------------------------------------------------------------
// Strict mode poison pills
BUILTIN(StrictFunctionCaller) {
HandleScope scope;
return Top::Throw(*Factory::NewTypeError("strict_function_caller",
HandleVector<Object>(NULL, 0)));
}
BUILTIN(StrictFunctionArguments) {
HandleScope scope;
return Top::Throw(*Factory::NewTypeError("strict_function_arguments",
HandleVector<Object>(NULL, 0)));
}
// -----------------------------------------------------------------------------
//
......
......@@ -58,7 +58,10 @@ enum BuiltinExtraArguments {
V(FastHandleApiCall, NO_EXTRA_ARGUMENTS) \
V(HandleApiCallConstruct, NEEDS_CALLED_FUNCTION) \
V(HandleApiCallAsFunction, NO_EXTRA_ARGUMENTS) \
V(HandleApiCallAsConstructor, NO_EXTRA_ARGUMENTS)
V(HandleApiCallAsConstructor, NO_EXTRA_ARGUMENTS) \
\
V(StrictFunctionCaller, NO_EXTRA_ARGUMENTS) \
V(StrictFunctionArguments, NO_EXTRA_ARGUMENTS)
// Define list of builtins implemented in assembly.
......
......@@ -78,8 +78,12 @@ enum ContextLookupFlags {
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_MAP_INDEX_STRICT, Map, function_map_strict) \
V(FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX, Map, function_without_prototype_map) \
V(FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX_STRICT, Map, \
function_without_prototype_map_strict) \
V(FUNCTION_INSTANCE_MAP_INDEX, Map, function_instance_map) \
V(FUNCTION_INSTANCE_MAP_INDEX_STRICT, Map, function_instance_map_strict) \
V(JS_ARRAY_MAP_INDEX, Map, js_array_map)\
V(REGEXP_RESULT_MAP_INDEX, Map, regexp_result_map)\
V(ARGUMENTS_BOILERPLATE_INDEX, JSObject, arguments_boilerplate) \
......@@ -185,8 +189,11 @@ class Context: public FixedArray {
JS_ARRAY_MAP_INDEX,
REGEXP_RESULT_MAP_INDEX,
FUNCTION_MAP_INDEX,
FUNCTION_MAP_INDEX_STRICT,
FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX,
FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX_STRICT,
FUNCTION_INSTANCE_MAP_INDEX,
FUNCTION_INSTANCE_MAP_INDEX_STRICT,
INITIAL_OBJECT_PROTOTYPE_INDEX,
BOOLEAN_FUNCTION_INDEX,
NUMBER_FUNCTION_INDEX,
......
......@@ -351,7 +351,12 @@ Handle<JSFunction> Factory::NewFunctionFromSharedFunctionInfo(
Handle<Context> context,
PretenureFlag pretenure) {
Handle<JSFunction> result = BaseNewFunctionFromSharedFunctionInfo(
function_info, Top::function_map(), pretenure);
function_info,
function_info->strict_mode()
? Top::function_map_strict()
: Top::function_map(),
pretenure);
result->set_context(*context);
int number_of_literals = function_info->num_literals();
Handle<FixedArray> literals =
......@@ -826,6 +831,24 @@ Handle<JSFunction> Factory::NewFunctionWithoutPrototype(Handle<String> name) {
}
Handle<JSFunction> Factory::NewFunctionWithoutPrototypeStrictHelper(
Handle<String> name) {
Handle<SharedFunctionInfo> function_share = NewSharedFunctionInfo(name);
CALL_HEAP_FUNCTION(Heap::AllocateFunction(
*Top::function_without_prototype_map_strict(),
*function_share,
*the_hole_value()),
JSFunction);
}
Handle<JSFunction> Factory::NewFunctionWithoutPrototypeStrict(
Handle<String> name) {
Handle<JSFunction> fun = NewFunctionWithoutPrototypeStrictHelper(name);
fun->set_context(Top::context()->global_context());
return fun;
}
Handle<Object> Factory::ToObject(Handle<Object> object) {
CALL_HEAP_FUNCTION(object->ToObject(), Object);
}
......
......@@ -232,6 +232,8 @@ class Factory : public AllStatic {
Handle<Object> prototype);
static Handle<JSFunction> NewFunctionWithoutPrototype(Handle<String> name);
static Handle<JSFunction> NewFunctionWithoutPrototypeStrict(
Handle<String> name);
static Handle<JSFunction> NewFunction(Handle<Object> super, bool is_global);
......@@ -408,6 +410,8 @@ class Factory : public AllStatic {
static Handle<JSFunction> NewFunctionWithoutPrototypeHelper(
Handle<String> name);
static Handle<JSFunction> NewFunctionWithoutPrototypeStrictHelper(
Handle<String> name);
static Handle<DescriptorArray> CopyAppendCallbackDescriptors(
Handle<DescriptorArray> array,
......
......@@ -362,6 +362,11 @@ Handle<Object> SetPrototype(Handle<JSObject> obj, Handle<Object> value) {
}
Handle<Object> PreventExtensions(Handle<JSObject> object) {
CALL_HEAP_FUNCTION(object->PreventExtensions(), Object);
}
Handle<Object> GetHiddenProperties(Handle<JSObject> obj,
bool create_if_needed) {
Object* holder = obj->BypassGlobalProxy();
......
......@@ -370,6 +370,7 @@ Handle<JSGlobalProxy> ReinitializeJSGlobalProxy(
Handle<Object> SetPrototype(Handle<JSFunction> function,
Handle<Object> prototype);
Handle<Object> PreventExtensions(Handle<JSObject> object);
// Does lazy compilation of the given function. Returns true on success and
// false if the compilation resulted in a stack overflow.
......
......@@ -4916,9 +4916,10 @@ Result CodeGenerator::InstantiateFunction(
// Use the fast case closure allocation code that allocates in new
// space for nested functions that don't need literals cloning.
if (scope()->is_function_scope() &&
if (!pretenure &&
scope()->is_function_scope() &&
function_info->num_literals() == 0 &&
!pretenure) {
!function_info->strict_mode()) { // Strict mode functions use slow path.
FastNewClosureStub stub;
frame()->EmitPush(Immediate(function_info));
return frame()->CallStub(&stub, 1);
......
......@@ -1017,9 +1017,10 @@ void FullCodeGenerator::EmitNewClosure(Handle<SharedFunctionInfo> info,
// doesn't just get a copy of the existing unoptimized code.
if (!FLAG_always_opt &&
!FLAG_prepare_always_opt &&
!pretenure &&
scope()->is_function_scope() &&
info->num_literals() == 0 &&
!pretenure) {
!info->strict_mode()) { // Strict mode functions go through slow path.
FastNewClosureStub stub;
__ push(Immediate(info));
__ CallStub(&stub);
......
......@@ -3693,7 +3693,8 @@ void LCodeGen::DoFunctionLiteral(LFunctionLiteral* instr) {
// space for nested functions that don't need literals cloning.
Handle<SharedFunctionInfo> shared_info = instr->shared_info();
bool pretenure = instr->hydrogen()->pretenure();
if (shared_info->num_literals() == 0 && !pretenure) {
if (!pretenure && shared_info->num_literals() == 0 &&
!shared_info->strict_mode()) {
FastNewClosureStub stub;
__ push(Immediate(shared_info));
CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr, false);
......
......@@ -230,6 +230,8 @@ function FormatMessage(message) {
strict_function: ["In strict mode code, functions can only be declared at top level or immediately within another function." ],
strict_read_only_property: ["Cannot assign to read only property '", "%0", "' of ", "%1"],
strict_cannot_assign: ["Cannot assign to read only '", "%0", "' in strict mode"],
strict_function_caller: ["Cannot access property 'caller' of a strict mode function"],
strict_function_arguments: ["Cannot access property 'arguments' of a strict mode function"],
};
}
var message_type = %MessageGetType(message);
......
......@@ -5536,12 +5536,21 @@ MaybeObject* JSFunction::SetPrototype(Object* value) {
Object* JSFunction::RemovePrototype() {
if (map() == context()->global_context()->function_without_prototype_map()) {
Map* no_prototype_map = shared()->strict_mode()
? context()->global_context()->function_without_prototype_map_strict()
: context()->global_context()->function_without_prototype_map();
if (map() == no_prototype_map) {
// Be idempotent.
return this;
}
ASSERT(map() == context()->global_context()->function_map());
set_map(context()->global_context()->function_without_prototype_map());
ASSERT(!shared()->strict_mode() ||
map() == context()->global_context()->function_map_strict());
ASSERT(shared()->strict_mode() ||
map() == context()->global_context()->function_map());
set_map(no_prototype_map);
set_prototype_or_initial_map(Heap::the_hole_value());
return this;
}
......
......@@ -8037,11 +8037,14 @@ static MaybeObject* Runtime_SetNewFunctionAttributes(Arguments args) {
HandleScope scope;
ASSERT(args.length() == 1);
CONVERT_ARG_CHECKED(JSFunction, func, 0);
ASSERT(func->map()->instance_type() ==
Top::function_instance_map()->instance_type());
ASSERT(func->map()->instance_size() ==
Top::function_instance_map()->instance_size());
func->set_map(*Top::function_instance_map());
Handle<Map> map = func->shared()->strict_mode()
? Top::function_instance_map_strict()
: Top::function_instance_map();
ASSERT(func->map()->instance_type() == map->instance_type());
ASSERT(func->map()->instance_size() == map->instance_size());
func->set_map(*map);
return *func;
}
......
......@@ -4261,9 +4261,10 @@ void CodeGenerator::InstantiateFunction(
// Use the fast case closure allocation code that allocates in new
// space for nested functions that don't need literals cloning.
if (scope()->is_function_scope() &&
if (!pretenure &&
scope()->is_function_scope() &&
function_info->num_literals() == 0 &&
!pretenure) {
!function_info->strict_mode()) { // Strict mode functions use slow path.
FastNewClosureStub stub;
frame_->Push(function_info);
Result answer = frame_->CallStub(&stub, 1);
......
......@@ -1039,9 +1039,10 @@ void FullCodeGenerator::EmitNewClosure(Handle<SharedFunctionInfo> info,
// doesn't just get a copy of the existing unoptimized code.
if (!FLAG_always_opt &&
!FLAG_prepare_always_opt &&
!pretenure &&
scope()->is_function_scope() &&
info->num_literals() == 0 &&
!pretenure) {
!info->strict_mode()) { // Strict mode functions use slow path.
FastNewClosureStub stub;
__ Push(info);
__ CallStub(&stub);
......
......@@ -3496,7 +3496,8 @@ void LCodeGen::DoFunctionLiteral(LFunctionLiteral* instr) {
// space for nested functions that don't need literals cloning.
Handle<SharedFunctionInfo> shared_info = instr->shared_info();
bool pretenure = instr->hydrogen()->pretenure();
if (shared_info->num_literals() == 0 && !pretenure) {
if (!pretenure && shared_info->num_literals() == 0 &&
!shared_info->strict_mode()) {
FastNewClosureStub stub;
__ Push(shared_info);
CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
......
......@@ -976,3 +976,47 @@ repeat(10, function() { testAssignToUndefined(false); });
assertEquals(["c", "d", "a", "b"], strict("a", "b"));
assertEquals(["c", "d", "c", "d"], nonstrict("a", "b"));
})();
(function TestStrictFunctionPills() {
function strict() {
"use strict";
}
assertThrows(function() { strict.caller; }, TypeError);
assertThrows(function() { strict.arguments; }, TypeError);
var another = new Function("'use strict'");
assertThrows(function() { another.caller; }, TypeError);
assertThrows(function() { another.arguments; }, TypeError);
var third = (function() { "use strict"; return function() {}; })();
assertThrows(function() { third.caller; }, TypeError);
assertThrows(function() { third.arguments; }, TypeError);
function CheckPill(pill) {
assertEquals("function", typeof pill);
assertInstanceof(pill, Function);
assertThrows(function() { pill.property = "value"; }, TypeError);
assertThrows(pill, TypeError);
assertEquals(pill.prototype, (function(){}).prototype);
var d = Object.getOwnPropertyDescriptor(pill, "prototype");
assertFalse(d.writable);
assertFalse(d.configurable);
assertFalse(d.enumerable);
}
function CheckPillDescriptor(func, name) {
var descriptor = Object.getOwnPropertyDescriptor(func, name);
CheckPill(descriptor.get)
CheckPill(descriptor.set);
assertFalse(descriptor.enumerable);
assertFalse(descriptor.configurable);
}
CheckPillDescriptor(strict, "caller");
CheckPillDescriptor(strict, "arguments");
CheckPillDescriptor(another, "caller");
CheckPillDescriptor(another, "arguments");
CheckPillDescriptor(third, "caller");
CheckPillDescriptor(third, "arguments");
})();
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