Remove the optimized construct stub.
R=mstarzinger@chromium.org Review URL: https://chromiumcodereview.appspot.com/15993016 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@14946 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
40a5fc747d
commit
5e8679beea
@ -3091,151 +3091,6 @@ Handle<Code> KeyedStoreStubCompiler::CompileStorePolymorphic(
|
||||
}
|
||||
|
||||
|
||||
Handle<Code> ConstructStubCompiler::CompileConstructStub(
|
||||
Handle<JSFunction> function) {
|
||||
// ----------- S t a t e -------------
|
||||
// -- r0 : argc
|
||||
// -- r1 : constructor
|
||||
// -- lr : return address
|
||||
// -- [sp] : last argument
|
||||
// -----------------------------------
|
||||
Label generic_stub_call;
|
||||
|
||||
// Use r7 for holding undefined which is used in several places below.
|
||||
__ LoadRoot(r7, Heap::kUndefinedValueRootIndex);
|
||||
|
||||
#ifdef ENABLE_DEBUGGER_SUPPORT
|
||||
// Check to see whether there are any break points in the function code. If
|
||||
// there are jump to the generic constructor stub which calls the actual
|
||||
// code for the function thereby hitting the break points.
|
||||
__ ldr(r2, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ ldr(r2, FieldMemOperand(r2, SharedFunctionInfo::kDebugInfoOffset));
|
||||
__ cmp(r2, r7);
|
||||
__ b(ne, &generic_stub_call);
|
||||
#endif
|
||||
|
||||
// Load the initial map and verify that it is in fact a map.
|
||||
// r1: constructor function
|
||||
// r7: undefined
|
||||
__ ldr(r2, FieldMemOperand(r1, JSFunction::kPrototypeOrInitialMapOffset));
|
||||
__ JumpIfSmi(r2, &generic_stub_call);
|
||||
__ CompareObjectType(r2, r3, r4, MAP_TYPE);
|
||||
__ b(ne, &generic_stub_call);
|
||||
|
||||
#ifdef DEBUG
|
||||
// Cannot construct functions this way.
|
||||
// r0: argc
|
||||
// r1: constructor function
|
||||
// r2: initial map
|
||||
// r7: undefined
|
||||
__ CompareInstanceType(r2, r3, JS_FUNCTION_TYPE);
|
||||
__ Check(ne, "Function constructed by construct stub.");
|
||||
#endif
|
||||
|
||||
// Now allocate the JSObject in new space.
|
||||
// r0: argc
|
||||
// r1: constructor function
|
||||
// r2: initial map
|
||||
// r7: undefined
|
||||
ASSERT(function->has_initial_map());
|
||||
__ ldrb(r3, FieldMemOperand(r2, Map::kInstanceSizeOffset));
|
||||
#ifdef DEBUG
|
||||
int instance_size = function->initial_map()->instance_size();
|
||||
__ cmp(r3, Operand(instance_size >> kPointerSizeLog2));
|
||||
__ Check(eq, "Instance size of initial map changed.");
|
||||
#endif
|
||||
__ Allocate(r3, r4, r5, r6, &generic_stub_call, SIZE_IN_WORDS);
|
||||
|
||||
// Allocated the JSObject, now initialize the fields. Map is set to initial
|
||||
// map and properties and elements are set to empty fixed array.
|
||||
// r0: argc
|
||||
// r1: constructor function
|
||||
// r2: initial map
|
||||
// r3: object size (in words)
|
||||
// r4: JSObject (not tagged)
|
||||
// r7: undefined
|
||||
__ LoadRoot(r6, Heap::kEmptyFixedArrayRootIndex);
|
||||
__ mov(r5, r4);
|
||||
ASSERT_EQ(0 * kPointerSize, JSObject::kMapOffset);
|
||||
__ str(r2, MemOperand(r5, kPointerSize, PostIndex));
|
||||
ASSERT_EQ(1 * kPointerSize, JSObject::kPropertiesOffset);
|
||||
__ str(r6, MemOperand(r5, kPointerSize, PostIndex));
|
||||
ASSERT_EQ(2 * kPointerSize, JSObject::kElementsOffset);
|
||||
__ str(r6, MemOperand(r5, kPointerSize, PostIndex));
|
||||
|
||||
// Calculate the location of the first argument. The stack contains only the
|
||||
// argc arguments.
|
||||
__ add(r1, sp, Operand(r0, LSL, kPointerSizeLog2));
|
||||
|
||||
// Fill all the in-object properties with undefined.
|
||||
// r0: argc
|
||||
// r1: first argument
|
||||
// r3: object size (in words)
|
||||
// r4: JSObject (not tagged)
|
||||
// r5: First in-object property of JSObject (not tagged)
|
||||
// r7: undefined
|
||||
// Fill the initialized properties with a constant value or a passed argument
|
||||
// depending on the this.x = ...; assignment in the function.
|
||||
Handle<SharedFunctionInfo> shared(function->shared());
|
||||
for (int i = 0; i < shared->this_property_assignments_count(); i++) {
|
||||
if (shared->IsThisPropertyAssignmentArgument(i)) {
|
||||
Label not_passed, next;
|
||||
// Check if the argument assigned to the property is actually passed.
|
||||
int arg_number = shared->GetThisPropertyAssignmentArgument(i);
|
||||
__ cmp(r0, Operand(arg_number));
|
||||
__ b(le, ¬_passed);
|
||||
// Argument passed - find it on the stack.
|
||||
__ ldr(r2, MemOperand(r1, (arg_number + 1) * -kPointerSize));
|
||||
__ str(r2, MemOperand(r5, kPointerSize, PostIndex));
|
||||
__ b(&next);
|
||||
__ bind(¬_passed);
|
||||
// Set the property to undefined.
|
||||
__ str(r7, MemOperand(r5, kPointerSize, PostIndex));
|
||||
__ bind(&next);
|
||||
} else {
|
||||
// Set the property to the constant value.
|
||||
Handle<Object> constant(shared->GetThisPropertyAssignmentConstant(i),
|
||||
isolate());
|
||||
__ mov(r2, Operand(constant));
|
||||
__ str(r2, MemOperand(r5, kPointerSize, PostIndex));
|
||||
}
|
||||
}
|
||||
|
||||
// Fill the unused in-object property fields with undefined.
|
||||
for (int i = shared->this_property_assignments_count();
|
||||
i < function->initial_map()->inobject_properties();
|
||||
i++) {
|
||||
__ str(r7, MemOperand(r5, kPointerSize, PostIndex));
|
||||
}
|
||||
|
||||
// r0: argc
|
||||
// r4: JSObject (not tagged)
|
||||
// Move argc to r1 and the JSObject to return to r0 and tag it.
|
||||
__ mov(r1, r0);
|
||||
__ mov(r0, r4);
|
||||
__ orr(r0, r0, Operand(kHeapObjectTag));
|
||||
|
||||
// r0: JSObject
|
||||
// r1: argc
|
||||
// Remove caller arguments and receiver from the stack and return.
|
||||
__ add(sp, sp, Operand(r1, LSL, kPointerSizeLog2));
|
||||
__ add(sp, sp, Operand(kPointerSize));
|
||||
Counters* counters = isolate()->counters();
|
||||
__ IncrementCounter(counters->constructed_objects(), 1, r1, r2);
|
||||
__ IncrementCounter(counters->constructed_objects_stub(), 1, r1, r2);
|
||||
__ Jump(lr);
|
||||
|
||||
// Jump to the generic stub in case the specialized code cannot handle the
|
||||
// construction.
|
||||
__ bind(&generic_stub_call);
|
||||
Handle<Code> code = isolate()->builtins()->JSConstructStubGeneric();
|
||||
__ Jump(code, RelocInfo::CODE_TARGET);
|
||||
|
||||
// Return the generated code.
|
||||
return GetCode();
|
||||
}
|
||||
|
||||
|
||||
#undef __
|
||||
#define __ ACCESS_MASM(masm)
|
||||
|
||||
|
30
src/ast.h
30
src/ast.h
@ -2161,12 +2161,6 @@ class FunctionLiteral: public Expression {
|
||||
int materialized_literal_count() { return materialized_literal_count_; }
|
||||
int expected_property_count() { return expected_property_count_; }
|
||||
int handler_count() { return handler_count_; }
|
||||
bool has_only_simple_this_property_assignments() {
|
||||
return HasOnlySimpleThisPropertyAssignments::decode(bitfield_);
|
||||
}
|
||||
Handle<FixedArray> this_property_assignments() {
|
||||
return this_property_assignments_;
|
||||
}
|
||||
int parameter_count() { return parameter_count_; }
|
||||
|
||||
bool AllowsLazyCompilation();
|
||||
@ -2221,8 +2215,6 @@ class FunctionLiteral: public Expression {
|
||||
int materialized_literal_count,
|
||||
int expected_property_count,
|
||||
int handler_count,
|
||||
bool has_only_simple_this_property_assignments,
|
||||
Handle<FixedArray> this_property_assignments,
|
||||
int parameter_count,
|
||||
Type type,
|
||||
ParameterFlag has_duplicate_parameters,
|
||||
@ -2233,7 +2225,6 @@ class FunctionLiteral: public Expression {
|
||||
name_(name),
|
||||
scope_(scope),
|
||||
body_(body),
|
||||
this_property_assignments_(this_property_assignments),
|
||||
inferred_name_(isolate->factory()->empty_string()),
|
||||
materialized_literal_count_(materialized_literal_count),
|
||||
expected_property_count_(expected_property_count),
|
||||
@ -2241,8 +2232,6 @@ class FunctionLiteral: public Expression {
|
||||
parameter_count_(parameter_count),
|
||||
function_token_position_(RelocInfo::kNoPosition) {
|
||||
bitfield_ =
|
||||
HasOnlySimpleThisPropertyAssignments::encode(
|
||||
has_only_simple_this_property_assignments) |
|
||||
IsExpression::encode(type != DECLARATION) |
|
||||
IsAnonymous::encode(type == ANONYMOUS_EXPRESSION) |
|
||||
Pretenure::encode(false) |
|
||||
@ -2256,7 +2245,6 @@ class FunctionLiteral: public Expression {
|
||||
Handle<String> name_;
|
||||
Scope* scope_;
|
||||
ZoneList<Statement*>* body_;
|
||||
Handle<FixedArray> this_property_assignments_;
|
||||
Handle<String> inferred_name_;
|
||||
AstProperties ast_properties_;
|
||||
|
||||
@ -2267,14 +2255,13 @@ class FunctionLiteral: public Expression {
|
||||
int function_token_position_;
|
||||
|
||||
unsigned bitfield_;
|
||||
class HasOnlySimpleThisPropertyAssignments: public BitField<bool, 0, 1> {};
|
||||
class IsExpression: public BitField<bool, 1, 1> {};
|
||||
class IsAnonymous: public BitField<bool, 2, 1> {};
|
||||
class Pretenure: public BitField<bool, 3, 1> {};
|
||||
class HasDuplicateParameters: public BitField<ParameterFlag, 4, 1> {};
|
||||
class IsFunction: public BitField<IsFunctionFlag, 5, 1> {};
|
||||
class IsParenthesized: public BitField<IsParenthesizedFlag, 6, 1> {};
|
||||
class IsGenerator: public BitField<IsGeneratorFlag, 7, 1> {};
|
||||
class IsExpression: public BitField<bool, 0, 1> {};
|
||||
class IsAnonymous: public BitField<bool, 1, 1> {};
|
||||
class Pretenure: public BitField<bool, 2, 1> {};
|
||||
class HasDuplicateParameters: public BitField<ParameterFlag, 3, 1> {};
|
||||
class IsFunction: public BitField<IsFunctionFlag, 4, 1> {};
|
||||
class IsParenthesized: public BitField<IsParenthesizedFlag, 5, 1> {};
|
||||
class IsGenerator: public BitField<IsGeneratorFlag, 6, 1> {};
|
||||
};
|
||||
|
||||
|
||||
@ -3097,8 +3084,6 @@ class AstNodeFactory BASE_EMBEDDED {
|
||||
int materialized_literal_count,
|
||||
int expected_property_count,
|
||||
int handler_count,
|
||||
bool has_only_simple_this_property_assignments,
|
||||
Handle<FixedArray> this_property_assignments,
|
||||
int parameter_count,
|
||||
FunctionLiteral::ParameterFlag has_duplicate_parameters,
|
||||
FunctionLiteral::Type type,
|
||||
@ -3108,7 +3093,6 @@ class AstNodeFactory BASE_EMBEDDED {
|
||||
FunctionLiteral* lit = new(zone_) FunctionLiteral(
|
||||
isolate_, name, scope, body,
|
||||
materialized_literal_count, expected_property_count, handler_count,
|
||||
has_only_simple_this_property_assignments, this_property_assignments,
|
||||
parameter_count, type, has_duplicate_parameters, is_function,
|
||||
is_parenthesized, is_generator);
|
||||
// Top-level literal doesn't count for the AST's properties.
|
||||
|
@ -771,13 +771,6 @@ static bool InstallFullCode(CompilationInfo* info) {
|
||||
int expected = lit->expected_property_count();
|
||||
SetExpectedNofPropertiesFromEstimate(shared, expected);
|
||||
|
||||
// Set the optimization hints after performing lazy compilation, as
|
||||
// these are not set when the function is set up as a lazily
|
||||
// compiled function.
|
||||
shared->SetThisPropertyAssignmentsInfo(
|
||||
lit->has_only_simple_this_property_assignments(),
|
||||
*lit->this_property_assignments());
|
||||
|
||||
// Check the function has compiled code.
|
||||
ASSERT(shared->is_compiled());
|
||||
shared->set_code_age(0);
|
||||
@ -1121,9 +1114,6 @@ void Compiler::SetFunctionInfo(Handle<SharedFunctionInfo> function_info,
|
||||
function_info->set_is_anonymous(lit->is_anonymous());
|
||||
function_info->set_is_toplevel(is_toplevel);
|
||||
function_info->set_inferred_name(*lit->inferred_name());
|
||||
function_info->SetThisPropertyAssignmentsInfo(
|
||||
lit->has_only_simple_this_property_assignments(),
|
||||
*lit->this_property_assignments());
|
||||
function_info->set_allows_lazy_compilation(lit->AllowsLazyCompilation());
|
||||
function_info->set_allows_lazy_compilation_without_context(
|
||||
lit->AllowsLazyCompilationWithoutContext());
|
||||
|
@ -1210,10 +1210,6 @@ void V8HeapExplorer::ExtractSharedFunctionInfoReferences(
|
||||
SetInternalReference(obj, entry,
|
||||
"inferred_name", shared->inferred_name(),
|
||||
SharedFunctionInfo::kInferredNameOffset);
|
||||
SetInternalReference(obj, entry,
|
||||
"this_property_assignments",
|
||||
shared->this_property_assignments(),
|
||||
SharedFunctionInfo::kThisPropertyAssignmentsOffset);
|
||||
SetWeakReference(obj, entry,
|
||||
1, shared->initial_map(),
|
||||
SharedFunctionInfo::kInitialMapOffset);
|
||||
|
58
src/heap.cc
58
src/heap.cc
@ -3377,7 +3377,6 @@ MaybeObject* Heap::AllocateSharedFunctionInfo(Object* name) {
|
||||
share->set_debug_info(undefined_value(), SKIP_WRITE_BARRIER);
|
||||
share->set_inferred_name(empty_string(), SKIP_WRITE_BARRIER);
|
||||
share->set_initial_map(undefined_value(), SKIP_WRITE_BARRIER);
|
||||
share->set_this_property_assignments(undefined_value(), SKIP_WRITE_BARRIER);
|
||||
share->set_ast_node_count(0);
|
||||
share->set_stress_deopt_counter(FLAG_deopt_every_n_times);
|
||||
share->set_counters(0);
|
||||
@ -3392,7 +3391,6 @@ MaybeObject* Heap::AllocateSharedFunctionInfo(Object* name) {
|
||||
share->set_function_token_position(0);
|
||||
// All compiler hints default to false or 0.
|
||||
share->set_compiler_hints(0);
|
||||
share->set_this_property_assignments_count(0);
|
||||
share->set_opt_count(0);
|
||||
|
||||
return share;
|
||||
@ -4161,20 +4159,6 @@ MaybeObject* Heap::AllocateArgumentsObject(Object* callee, int length) {
|
||||
}
|
||||
|
||||
|
||||
static bool HasDuplicates(DescriptorArray* descriptors) {
|
||||
int count = descriptors->number_of_descriptors();
|
||||
if (count > 1) {
|
||||
Name* prev_key = descriptors->GetKey(0);
|
||||
for (int i = 1; i != count; i++) {
|
||||
Name* current_key = descriptors->GetKey(i);
|
||||
if (prev_key == current_key) return true;
|
||||
prev_key = current_key;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
MaybeObject* Heap::AllocateInitialMap(JSFunction* fun) {
|
||||
ASSERT(!fun->has_initial_map());
|
||||
|
||||
@ -4209,48 +4193,6 @@ MaybeObject* Heap::AllocateInitialMap(JSFunction* fun) {
|
||||
map->set_prototype(prototype);
|
||||
ASSERT(map->has_fast_object_elements());
|
||||
|
||||
// If the function has only simple this property assignments add
|
||||
// field descriptors for these to the initial map as the object
|
||||
// cannot be constructed without having these properties. Guard by
|
||||
// the inline_new flag so we only change the map if we generate a
|
||||
// specialized construct stub.
|
||||
ASSERT(in_object_properties <= Map::kMaxPreAllocatedPropertyFields);
|
||||
if (!fun->shared()->is_generator() &&
|
||||
fun->shared()->CanGenerateInlineConstructor(prototype)) {
|
||||
int count = fun->shared()->this_property_assignments_count();
|
||||
if (count > in_object_properties) {
|
||||
// Inline constructor can only handle inobject properties.
|
||||
fun->shared()->ForbidInlineConstructor();
|
||||
} else {
|
||||
DescriptorArray* descriptors;
|
||||
MaybeObject* maybe_descriptors = DescriptorArray::Allocate(count);
|
||||
if (!maybe_descriptors->To(&descriptors)) return maybe_descriptors;
|
||||
|
||||
DescriptorArray::WhitenessWitness witness(descriptors);
|
||||
for (int i = 0; i < count; i++) {
|
||||
String* name = fun->shared()->GetThisPropertyAssignmentName(i);
|
||||
ASSERT(name->IsInternalizedString());
|
||||
// TODO(verwaest): Since we cannot update the boilerplate's map yet,
|
||||
// initialize to the worst case.
|
||||
FieldDescriptor field(name, i, NONE, Representation::Tagged());
|
||||
descriptors->Set(i, &field, witness);
|
||||
}
|
||||
descriptors->Sort();
|
||||
|
||||
// The descriptors may contain duplicates because the compiler does not
|
||||
// guarantee the uniqueness of property names (it would have required
|
||||
// quadratic time). Once the descriptors are sorted we can check for
|
||||
// duplicates in linear time.
|
||||
if (HasDuplicates(descriptors)) {
|
||||
fun->shared()->ForbidInlineConstructor();
|
||||
} else {
|
||||
map->InitializeDescriptors(descriptors);
|
||||
map->set_pre_allocated_property_fields(count);
|
||||
map->set_unused_property_fields(in_object_properties - count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!fun->shared()->is_generator()) {
|
||||
fun->shared()->StartInobjectSlackTracking(map);
|
||||
}
|
||||
|
@ -3178,145 +3178,6 @@ Handle<Code> BaseLoadStubCompiler::CompilePolymorphicIC(
|
||||
}
|
||||
|
||||
|
||||
// Specialized stub for constructing objects from functions which only have only
|
||||
// simple assignments of the form this.x = ...; in their body.
|
||||
Handle<Code> ConstructStubCompiler::CompileConstructStub(
|
||||
Handle<JSFunction> function) {
|
||||
// ----------- S t a t e -------------
|
||||
// -- eax : argc
|
||||
// -- edi : constructor
|
||||
// -- esp[0] : return address
|
||||
// -- esp[4] : last argument
|
||||
// -----------------------------------
|
||||
Label generic_stub_call;
|
||||
#ifdef ENABLE_DEBUGGER_SUPPORT
|
||||
// Check to see whether there are any break points in the function code. If
|
||||
// there are jump to the generic constructor stub which calls the actual
|
||||
// code for the function thereby hitting the break points.
|
||||
__ mov(ebx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ mov(ebx, FieldOperand(ebx, SharedFunctionInfo::kDebugInfoOffset));
|
||||
__ cmp(ebx, factory()->undefined_value());
|
||||
__ j(not_equal, &generic_stub_call);
|
||||
#endif
|
||||
|
||||
// Load the initial map and verify that it is in fact a map.
|
||||
// edi: constructor
|
||||
__ mov(ebx, FieldOperand(edi, JSFunction::kPrototypeOrInitialMapOffset));
|
||||
// Will both indicate a NULL and a Smi.
|
||||
__ JumpIfSmi(ebx, &generic_stub_call);
|
||||
__ CmpObjectType(ebx, MAP_TYPE, ecx);
|
||||
__ j(not_equal, &generic_stub_call);
|
||||
|
||||
#ifdef DEBUG
|
||||
// Cannot construct functions this way.
|
||||
// ebx: initial map
|
||||
__ CmpInstanceType(ebx, JS_FUNCTION_TYPE);
|
||||
__ Check(not_equal, "Function constructed by construct stub.");
|
||||
#endif
|
||||
|
||||
// Now allocate the JSObject on the heap by moving the new space allocation
|
||||
// top forward.
|
||||
// ebx: initial map
|
||||
ASSERT(function->has_initial_map());
|
||||
int instance_size = function->initial_map()->instance_size();
|
||||
#ifdef DEBUG
|
||||
__ movzx_b(ecx, FieldOperand(ebx, Map::kInstanceSizeOffset));
|
||||
__ shl(ecx, kPointerSizeLog2);
|
||||
__ cmp(ecx, Immediate(instance_size));
|
||||
__ Check(equal, "Instance size of initial map changed.");
|
||||
#endif
|
||||
__ Allocate(instance_size, edx, ecx, no_reg, &generic_stub_call,
|
||||
NO_ALLOCATION_FLAGS);
|
||||
|
||||
// Allocated the JSObject, now initialize the fields and add the heap tag.
|
||||
// ebx: initial map
|
||||
// edx: JSObject (untagged)
|
||||
__ mov(Operand(edx, JSObject::kMapOffset), ebx);
|
||||
__ mov(ebx, factory()->empty_fixed_array());
|
||||
__ mov(Operand(edx, JSObject::kPropertiesOffset), ebx);
|
||||
__ mov(Operand(edx, JSObject::kElementsOffset), ebx);
|
||||
|
||||
// Push the allocated object to the stack. This is the object that will be
|
||||
// returned (after it is tagged).
|
||||
__ push(edx);
|
||||
|
||||
// eax: argc
|
||||
// edx: JSObject (untagged)
|
||||
// Load the address of the first in-object property into edx.
|
||||
__ lea(edx, Operand(edx, JSObject::kHeaderSize));
|
||||
// Calculate the location of the first argument. The stack contains the
|
||||
// allocated object and the return address on top of the argc arguments.
|
||||
__ lea(ecx, Operand(esp, eax, times_4, 1 * kPointerSize));
|
||||
|
||||
// Use edi for holding undefined which is used in several places below.
|
||||
__ mov(edi, factory()->undefined_value());
|
||||
|
||||
// eax: argc
|
||||
// ecx: first argument
|
||||
// edx: first in-object property of the JSObject
|
||||
// edi: undefined
|
||||
// Fill the initialized properties with a constant value or a passed argument
|
||||
// depending on the this.x = ...; assignment in the function.
|
||||
Handle<SharedFunctionInfo> shared(function->shared());
|
||||
for (int i = 0; i < shared->this_property_assignments_count(); i++) {
|
||||
if (shared->IsThisPropertyAssignmentArgument(i)) {
|
||||
// Check if the argument assigned to the property is actually passed.
|
||||
// If argument is not passed the property is set to undefined,
|
||||
// otherwise find it on the stack.
|
||||
int arg_number = shared->GetThisPropertyAssignmentArgument(i);
|
||||
__ mov(ebx, edi);
|
||||
__ cmp(eax, arg_number);
|
||||
if (CpuFeatures::IsSupported(CMOV)) {
|
||||
CpuFeatureScope use_cmov(masm(), CMOV);
|
||||
__ cmov(above, ebx, Operand(ecx, arg_number * -kPointerSize));
|
||||
} else {
|
||||
Label not_passed;
|
||||
__ j(below_equal, ¬_passed);
|
||||
__ mov(ebx, Operand(ecx, arg_number * -kPointerSize));
|
||||
__ bind(¬_passed);
|
||||
}
|
||||
// Store value in the property.
|
||||
__ mov(Operand(edx, i * kPointerSize), ebx);
|
||||
} else {
|
||||
// Set the property to the constant value.
|
||||
Handle<Object> constant(shared->GetThisPropertyAssignmentConstant(i),
|
||||
isolate());
|
||||
__ mov(Operand(edx, i * kPointerSize), Immediate(constant));
|
||||
}
|
||||
}
|
||||
|
||||
// Fill the unused in-object property fields with undefined.
|
||||
for (int i = shared->this_property_assignments_count();
|
||||
i < function->initial_map()->inobject_properties();
|
||||
i++) {
|
||||
__ mov(Operand(edx, i * kPointerSize), edi);
|
||||
}
|
||||
|
||||
// Move argc to ebx and retrieve and tag the JSObject to return.
|
||||
__ mov(ebx, eax);
|
||||
__ pop(eax);
|
||||
__ or_(eax, Immediate(kHeapObjectTag));
|
||||
|
||||
// Remove caller arguments and receiver from the stack and return.
|
||||
__ pop(ecx);
|
||||
__ lea(esp, Operand(esp, ebx, times_pointer_size, 1 * kPointerSize));
|
||||
__ push(ecx);
|
||||
Counters* counters = isolate()->counters();
|
||||
__ IncrementCounter(counters->constructed_objects(), 1);
|
||||
__ IncrementCounter(counters->constructed_objects_stub(), 1);
|
||||
__ ret(0);
|
||||
|
||||
// Jump to the generic stub in case the specialized code cannot handle the
|
||||
// construction.
|
||||
__ bind(&generic_stub_call);
|
||||
Handle<Code> code = isolate()->builtins()->JSConstructStubGeneric();
|
||||
__ jmp(code, RelocInfo::CODE_TARGET);
|
||||
|
||||
// Return the generated code.
|
||||
return GetCode();
|
||||
}
|
||||
|
||||
|
||||
#undef __
|
||||
#define __ ACCESS_MASM(masm)
|
||||
|
||||
|
@ -3143,157 +3143,6 @@ Handle<Code> KeyedStoreStubCompiler::CompileStorePolymorphic(
|
||||
}
|
||||
|
||||
|
||||
Handle<Code> ConstructStubCompiler::CompileConstructStub(
|
||||
Handle<JSFunction> function) {
|
||||
// a0 : argc
|
||||
// a1 : constructor
|
||||
// ra : return address
|
||||
// [sp] : last argument
|
||||
Label generic_stub_call;
|
||||
|
||||
// Use t7 for holding undefined which is used in several places below.
|
||||
__ LoadRoot(t7, Heap::kUndefinedValueRootIndex);
|
||||
|
||||
#ifdef ENABLE_DEBUGGER_SUPPORT
|
||||
// Check to see whether there are any break points in the function code. If
|
||||
// there are jump to the generic constructor stub which calls the actual
|
||||
// code for the function thereby hitting the break points.
|
||||
__ lw(t5, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ lw(a2, FieldMemOperand(t5, SharedFunctionInfo::kDebugInfoOffset));
|
||||
__ Branch(&generic_stub_call, ne, a2, Operand(t7));
|
||||
#endif
|
||||
|
||||
// Load the initial map and verify that it is in fact a map.
|
||||
// a1: constructor function
|
||||
// t7: undefined
|
||||
__ lw(a2, FieldMemOperand(a1, JSFunction::kPrototypeOrInitialMapOffset));
|
||||
__ JumpIfSmi(a2, &generic_stub_call);
|
||||
__ GetObjectType(a2, a3, t0);
|
||||
__ Branch(&generic_stub_call, ne, t0, Operand(MAP_TYPE));
|
||||
|
||||
#ifdef DEBUG
|
||||
// Cannot construct functions this way.
|
||||
// a0: argc
|
||||
// a1: constructor function
|
||||
// a2: initial map
|
||||
// t7: undefined
|
||||
__ lbu(a3, FieldMemOperand(a2, Map::kInstanceTypeOffset));
|
||||
__ Check(ne, "Function constructed by construct stub.",
|
||||
a3, Operand(JS_FUNCTION_TYPE));
|
||||
#endif
|
||||
|
||||
// Now allocate the JSObject in new space.
|
||||
// a0: argc
|
||||
// a1: constructor function
|
||||
// a2: initial map
|
||||
// t7: undefined
|
||||
ASSERT(function->has_initial_map());
|
||||
__ lbu(a3, FieldMemOperand(a2, Map::kInstanceSizeOffset));
|
||||
#ifdef DEBUG
|
||||
int instance_size = function->initial_map()->instance_size();
|
||||
__ Check(eq, "Instance size of initial map changed.",
|
||||
a3, Operand(instance_size >> kPointerSizeLog2));
|
||||
#endif
|
||||
__ Allocate(a3, t4, t5, t6, &generic_stub_call, SIZE_IN_WORDS);
|
||||
|
||||
// Allocated the JSObject, now initialize the fields. Map is set to initial
|
||||
// map and properties and elements are set to empty fixed array.
|
||||
// a0: argc
|
||||
// a1: constructor function
|
||||
// a2: initial map
|
||||
// a3: object size (in words)
|
||||
// t4: JSObject (not tagged)
|
||||
// t7: undefined
|
||||
__ LoadRoot(t6, Heap::kEmptyFixedArrayRootIndex);
|
||||
__ mov(t5, t4);
|
||||
__ sw(a2, MemOperand(t5, JSObject::kMapOffset));
|
||||
__ sw(t6, MemOperand(t5, JSObject::kPropertiesOffset));
|
||||
__ sw(t6, MemOperand(t5, JSObject::kElementsOffset));
|
||||
__ Addu(t5, t5, Operand(3 * kPointerSize));
|
||||
ASSERT_EQ(0 * kPointerSize, JSObject::kMapOffset);
|
||||
ASSERT_EQ(1 * kPointerSize, JSObject::kPropertiesOffset);
|
||||
ASSERT_EQ(2 * kPointerSize, JSObject::kElementsOffset);
|
||||
|
||||
|
||||
// Calculate the location of the first argument. The stack contains only the
|
||||
// argc arguments.
|
||||
__ sll(a1, a0, kPointerSizeLog2);
|
||||
__ Addu(a1, a1, sp);
|
||||
|
||||
// Fill all the in-object properties with undefined.
|
||||
// a0: argc
|
||||
// a1: first argument
|
||||
// a3: object size (in words)
|
||||
// t4: JSObject (not tagged)
|
||||
// t5: First in-object property of JSObject (not tagged)
|
||||
// t7: undefined
|
||||
// Fill the initialized properties with a constant value or a passed argument
|
||||
// depending on the this.x = ...; assignment in the function.
|
||||
Handle<SharedFunctionInfo> shared(function->shared());
|
||||
for (int i = 0; i < shared->this_property_assignments_count(); i++) {
|
||||
if (shared->IsThisPropertyAssignmentArgument(i)) {
|
||||
Label not_passed, next;
|
||||
// Check if the argument assigned to the property is actually passed.
|
||||
int arg_number = shared->GetThisPropertyAssignmentArgument(i);
|
||||
__ Branch(¬_passed, less_equal, a0, Operand(arg_number));
|
||||
// Argument passed - find it on the stack.
|
||||
__ lw(a2, MemOperand(a1, (arg_number + 1) * -kPointerSize));
|
||||
__ sw(a2, MemOperand(t5));
|
||||
__ Addu(t5, t5, kPointerSize);
|
||||
__ jmp(&next);
|
||||
__ bind(¬_passed);
|
||||
// Set the property to undefined.
|
||||
__ sw(t7, MemOperand(t5));
|
||||
__ Addu(t5, t5, Operand(kPointerSize));
|
||||
__ bind(&next);
|
||||
} else {
|
||||
// Set the property to the constant value.
|
||||
Handle<Object> constant(
|
||||
shared->GetThisPropertyAssignmentConstant(i), isolate());
|
||||
__ li(a2, Operand(constant));
|
||||
__ sw(a2, MemOperand(t5));
|
||||
__ Addu(t5, t5, kPointerSize);
|
||||
}
|
||||
}
|
||||
|
||||
// Fill the unused in-object property fields with undefined.
|
||||
for (int i = shared->this_property_assignments_count();
|
||||
i < function->initial_map()->inobject_properties();
|
||||
i++) {
|
||||
__ sw(t7, MemOperand(t5));
|
||||
__ Addu(t5, t5, kPointerSize);
|
||||
}
|
||||
|
||||
// a0: argc
|
||||
// t4: JSObject (not tagged)
|
||||
// Move argc to a1 and the JSObject to return to v0 and tag it.
|
||||
__ mov(a1, a0);
|
||||
__ mov(v0, t4);
|
||||
__ Or(v0, v0, Operand(kHeapObjectTag));
|
||||
|
||||
// v0: JSObject
|
||||
// a1: argc
|
||||
// Remove caller arguments and receiver from the stack and return.
|
||||
__ sll(t0, a1, kPointerSizeLog2);
|
||||
__ Addu(sp, sp, t0);
|
||||
__ Addu(sp, sp, Operand(kPointerSize));
|
||||
Counters* counters = isolate()->counters();
|
||||
__ IncrementCounter(counters->constructed_objects(), 1, a1, a2);
|
||||
__ IncrementCounter(counters->constructed_objects_stub(), 1, a1, a2);
|
||||
__ Ret();
|
||||
|
||||
// Jump to the generic stub in case the specialized code cannot handle the
|
||||
// construction.
|
||||
__ bind(&generic_stub_call);
|
||||
Handle<Code> generic_construct_stub =
|
||||
isolate()->builtins()->JSConstructStubGeneric();
|
||||
__ Jump(generic_construct_stub, RelocInfo::CODE_TARGET);
|
||||
|
||||
// Return the generated code.
|
||||
return GetCode();
|
||||
}
|
||||
|
||||
|
||||
#undef __
|
||||
#define __ ACCESS_MASM(masm)
|
||||
|
||||
|
@ -4460,8 +4460,6 @@ ACCESSORS(SharedFunctionInfo, function_data, Object, kFunctionDataOffset)
|
||||
ACCESSORS(SharedFunctionInfo, script, Object, kScriptOffset)
|
||||
ACCESSORS(SharedFunctionInfo, debug_info, Object, kDebugInfoOffset)
|
||||
ACCESSORS(SharedFunctionInfo, inferred_name, String, kInferredNameOffset)
|
||||
ACCESSORS(SharedFunctionInfo, this_property_assignments, Object,
|
||||
kThisPropertyAssignmentsOffset)
|
||||
SMI_ACCESSORS(SharedFunctionInfo, ast_node_count, kAstNodeCountOffset)
|
||||
|
||||
|
||||
@ -4478,10 +4476,6 @@ BOOL_ACCESSORS(SharedFunctionInfo, start_position_and_type, is_expression,
|
||||
BOOL_ACCESSORS(SharedFunctionInfo, start_position_and_type, is_toplevel,
|
||||
kIsTopLevelBit)
|
||||
|
||||
BOOL_GETTER(SharedFunctionInfo,
|
||||
compiler_hints,
|
||||
has_only_simple_this_property_assignments,
|
||||
kHasOnlySimpleThisPropertyAssignments)
|
||||
BOOL_ACCESSORS(SharedFunctionInfo,
|
||||
compiler_hints,
|
||||
allows_lazy_compilation,
|
||||
@ -4514,8 +4508,6 @@ SMI_ACCESSORS(SharedFunctionInfo, function_token_position,
|
||||
kFunctionTokenPositionOffset)
|
||||
SMI_ACCESSORS(SharedFunctionInfo, compiler_hints,
|
||||
kCompilerHintsOffset)
|
||||
SMI_ACCESSORS(SharedFunctionInfo, this_property_assignments_count,
|
||||
kThisPropertyAssignmentsCountOffset)
|
||||
SMI_ACCESSORS(SharedFunctionInfo, opt_count, kOptCountOffset)
|
||||
SMI_ACCESSORS(SharedFunctionInfo, counters, kCountersOffset)
|
||||
SMI_ACCESSORS(SharedFunctionInfo,
|
||||
@ -4567,13 +4559,10 @@ PSEUDO_SMI_ACCESSORS_HI(SharedFunctionInfo,
|
||||
compiler_hints,
|
||||
kCompilerHintsOffset)
|
||||
|
||||
PSEUDO_SMI_ACCESSORS_LO(SharedFunctionInfo,
|
||||
this_property_assignments_count,
|
||||
kThisPropertyAssignmentsCountOffset)
|
||||
PSEUDO_SMI_ACCESSORS_HI(SharedFunctionInfo, opt_count, kOptCountOffset)
|
||||
PSEUDO_SMI_ACCESSORS_LO(SharedFunctionInfo, opt_count, kOptCountOffset)
|
||||
|
||||
PSEUDO_SMI_ACCESSORS_LO(SharedFunctionInfo, counters, kCountersOffset)
|
||||
PSEUDO_SMI_ACCESSORS_HI(SharedFunctionInfo,
|
||||
PSEUDO_SMI_ACCESSORS_HI(SharedFunctionInfo, counters, kCountersOffset)
|
||||
PSEUDO_SMI_ACCESSORS_LO(SharedFunctionInfo,
|
||||
stress_deopt_counter,
|
||||
kStressDeoptCounterOffset)
|
||||
#endif
|
||||
|
@ -885,10 +885,6 @@ void SharedFunctionInfo::SharedFunctionInfoPrint(FILE* out) {
|
||||
PrintF(out, "\n - debug info = ");
|
||||
debug_info()->ShortPrint(out);
|
||||
PrintF(out, "\n - length = %d", length());
|
||||
PrintF(out, "\n - has_only_simple_this_property_assignments = %d",
|
||||
has_only_simple_this_property_assignments());
|
||||
PrintF(out, "\n - this_property_assignments = ");
|
||||
this_property_assignments()->ShortPrint(out);
|
||||
PrintF(out, "\n - optimized_code_map = ");
|
||||
optimized_code_map()->ShortPrint(out);
|
||||
PrintF(out, "\n");
|
||||
|
108
src/objects.cc
108
src/objects.cc
@ -9647,114 +9647,6 @@ int SharedFunctionInfo::CalculateInObjectProperties() {
|
||||
}
|
||||
|
||||
|
||||
bool SharedFunctionInfo::CanGenerateInlineConstructor(Object* prototype) {
|
||||
// Check the basic conditions for generating inline constructor code.
|
||||
if (!FLAG_inline_new
|
||||
|| !has_only_simple_this_property_assignments()
|
||||
|| is_generator()
|
||||
|| this_property_assignments_count() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Isolate* isolate = GetIsolate();
|
||||
Heap* heap = isolate->heap();
|
||||
|
||||
// Traverse the proposed prototype chain looking for properties of the
|
||||
// same names as are set by the inline constructor.
|
||||
for (Object* obj = prototype;
|
||||
obj != heap->null_value();
|
||||
obj = obj->GetPrototype(isolate)) {
|
||||
JSReceiver* receiver = JSReceiver::cast(obj);
|
||||
for (int i = 0; i < this_property_assignments_count(); i++) {
|
||||
LookupResult result(heap->isolate());
|
||||
String* name = GetThisPropertyAssignmentName(i);
|
||||
receiver->LocalLookup(name, &result);
|
||||
if (result.IsFound()) {
|
||||
switch (result.type()) {
|
||||
case NORMAL:
|
||||
case FIELD:
|
||||
case CONSTANT_FUNCTION:
|
||||
break;
|
||||
case INTERCEPTOR:
|
||||
case CALLBACKS:
|
||||
case HANDLER:
|
||||
return false;
|
||||
case TRANSITION:
|
||||
case NONEXISTENT:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void SharedFunctionInfo::ForbidInlineConstructor() {
|
||||
set_compiler_hints(BooleanBit::set(compiler_hints(),
|
||||
kHasOnlySimpleThisPropertyAssignments,
|
||||
false));
|
||||
}
|
||||
|
||||
|
||||
void SharedFunctionInfo::SetThisPropertyAssignmentsInfo(
|
||||
bool only_simple_this_property_assignments,
|
||||
FixedArray* assignments) {
|
||||
set_compiler_hints(BooleanBit::set(compiler_hints(),
|
||||
kHasOnlySimpleThisPropertyAssignments,
|
||||
only_simple_this_property_assignments));
|
||||
set_this_property_assignments(assignments);
|
||||
set_this_property_assignments_count(assignments->length() / 3);
|
||||
}
|
||||
|
||||
|
||||
void SharedFunctionInfo::ClearThisPropertyAssignmentsInfo() {
|
||||
Heap* heap = GetHeap();
|
||||
set_compiler_hints(BooleanBit::set(compiler_hints(),
|
||||
kHasOnlySimpleThisPropertyAssignments,
|
||||
false));
|
||||
set_this_property_assignments(heap->undefined_value());
|
||||
set_this_property_assignments_count(0);
|
||||
}
|
||||
|
||||
|
||||
String* SharedFunctionInfo::GetThisPropertyAssignmentName(int index) {
|
||||
Object* obj = this_property_assignments();
|
||||
ASSERT(obj->IsFixedArray());
|
||||
ASSERT(index < this_property_assignments_count());
|
||||
obj = FixedArray::cast(obj)->get(index * 3);
|
||||
ASSERT(obj->IsString());
|
||||
return String::cast(obj);
|
||||
}
|
||||
|
||||
|
||||
bool SharedFunctionInfo::IsThisPropertyAssignmentArgument(int index) {
|
||||
Object* obj = this_property_assignments();
|
||||
ASSERT(obj->IsFixedArray());
|
||||
ASSERT(index < this_property_assignments_count());
|
||||
obj = FixedArray::cast(obj)->get(index * 3 + 1);
|
||||
return Smi::cast(obj)->value() != -1;
|
||||
}
|
||||
|
||||
|
||||
int SharedFunctionInfo::GetThisPropertyAssignmentArgument(int index) {
|
||||
ASSERT(IsThisPropertyAssignmentArgument(index));
|
||||
Object* obj =
|
||||
FixedArray::cast(this_property_assignments())->get(index * 3 + 1);
|
||||
return Smi::cast(obj)->value();
|
||||
}
|
||||
|
||||
|
||||
Object* SharedFunctionInfo::GetThisPropertyAssignmentConstant(int index) {
|
||||
ASSERT(!IsThisPropertyAssignmentArgument(index));
|
||||
Object* obj =
|
||||
FixedArray::cast(this_property_assignments())->get(index * 3 + 2);
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
// Support function for printing the source code to a StringStream
|
||||
// without any allocation in the heap.
|
||||
void SharedFunctionInfo::SourceCodePrint(StringStream* accumulator,
|
||||
|
@ -6077,18 +6077,6 @@ class SharedFunctionInfo: public HeapObject {
|
||||
inline int ic_age();
|
||||
inline void set_ic_age(int age);
|
||||
|
||||
// Add information on assignments of the form this.x = ...;
|
||||
void SetThisPropertyAssignmentsInfo(
|
||||
bool has_only_simple_this_property_assignments,
|
||||
FixedArray* this_property_assignments);
|
||||
|
||||
// Clear information on assignments of the form this.x = ...;
|
||||
void ClearThisPropertyAssignmentsInfo();
|
||||
|
||||
// Indicate that this function only consists of assignments of the form
|
||||
// this.x = y; where y is either a constant or refers to an argument.
|
||||
inline bool has_only_simple_this_property_assignments();
|
||||
|
||||
// Indicates if this function can be lazy compiled.
|
||||
// This is used to determine if we can safely flush code from a function
|
||||
// when doing GC if we expect that the function will no longer be used.
|
||||
@ -6189,24 +6177,6 @@ class SharedFunctionInfo: public HeapObject {
|
||||
// disabled).
|
||||
bool VerifyBailoutId(BailoutId id);
|
||||
|
||||
// Check whether a inlined constructor can be generated with the given
|
||||
// prototype.
|
||||
bool CanGenerateInlineConstructor(Object* prototype);
|
||||
|
||||
// Prevents further attempts to generate inline constructors.
|
||||
// To be called if generation failed for any reason.
|
||||
void ForbidInlineConstructor();
|
||||
|
||||
// For functions which only contains this property assignments this provides
|
||||
// access to the names for the properties assigned.
|
||||
DECL_ACCESSORS(this_property_assignments, Object)
|
||||
inline int this_property_assignments_count();
|
||||
inline void set_this_property_assignments_count(int value);
|
||||
String* GetThisPropertyAssignmentName(int index);
|
||||
bool IsThisPropertyAssignmentArgument(int index);
|
||||
int GetThisPropertyAssignmentArgument(int index);
|
||||
Object* GetThisPropertyAssignmentConstant(int index);
|
||||
|
||||
// [source code]: Source code for the function.
|
||||
bool HasSourceCode();
|
||||
Handle<Object> GetSourceCode();
|
||||
@ -6276,12 +6246,10 @@ class SharedFunctionInfo: public HeapObject {
|
||||
static const int kInferredNameOffset = kDebugInfoOffset + kPointerSize;
|
||||
static const int kInitialMapOffset =
|
||||
kInferredNameOffset + kPointerSize;
|
||||
static const int kThisPropertyAssignmentsOffset =
|
||||
kInitialMapOffset + kPointerSize;
|
||||
// ast_node_count is a Smi field. It could be grouped with another Smi field
|
||||
// into a PSEUDO_SMI_ACCESSORS pair (on x64), if one becomes available.
|
||||
static const int kAstNodeCountOffset =
|
||||
kThisPropertyAssignmentsOffset + kPointerSize;
|
||||
kInitialMapOffset + kPointerSize;
|
||||
#if V8_HOST_ARCH_32_BIT
|
||||
// Smi fields.
|
||||
static const int kLengthOffset =
|
||||
@ -6299,10 +6267,7 @@ class SharedFunctionInfo: public HeapObject {
|
||||
kEndPositionOffset + kPointerSize;
|
||||
static const int kCompilerHintsOffset =
|
||||
kFunctionTokenPositionOffset + kPointerSize;
|
||||
static const int kThisPropertyAssignmentsCountOffset =
|
||||
kCompilerHintsOffset + kPointerSize;
|
||||
static const int kOptCountOffset =
|
||||
kThisPropertyAssignmentsCountOffset + kPointerSize;
|
||||
static const int kOptCountOffset = kCompilerHintsOffset + kPointerSize;
|
||||
static const int kCountersOffset = kOptCountOffset + kPointerSize;
|
||||
static const int kStressDeoptCounterOffset = kCountersOffset + kPointerSize;
|
||||
|
||||
@ -6338,10 +6303,7 @@ class SharedFunctionInfo: public HeapObject {
|
||||
static const int kCompilerHintsOffset =
|
||||
kFunctionTokenPositionOffset + kIntSize;
|
||||
|
||||
static const int kThisPropertyAssignmentsCountOffset =
|
||||
kCompilerHintsOffset + kIntSize;
|
||||
static const int kOptCountOffset =
|
||||
kThisPropertyAssignmentsCountOffset + kIntSize;
|
||||
static const int kOptCountOffset = kCompilerHintsOffset + kIntSize;
|
||||
|
||||
static const int kCountersOffset = kOptCountOffset + kIntSize;
|
||||
static const int kStressDeoptCounterOffset = kCountersOffset + kIntSize;
|
||||
@ -6365,7 +6327,7 @@ class SharedFunctionInfo: public HeapObject {
|
||||
static const int kAlignedSize = POINTER_SIZE_ALIGN(kSize);
|
||||
|
||||
typedef FixedBodyDescriptor<kNameOffset,
|
||||
kThisPropertyAssignmentsOffset + kPointerSize,
|
||||
kInitialMapOffset + kPointerSize,
|
||||
kSize> BodyDescriptor;
|
||||
|
||||
// Bit positions in start_position_and_type.
|
||||
@ -6381,7 +6343,6 @@ class SharedFunctionInfo: public HeapObject {
|
||||
static const int kCodeAgeMask = (1 << kCodeAgeSize) - 1;
|
||||
|
||||
enum CompilerHints {
|
||||
kHasOnlySimpleThisPropertyAssignments,
|
||||
kAllowLazyCompilation,
|
||||
kAllowLazyCompilationWithoutContext,
|
||||
kLiveObjectsMayExist,
|
||||
|
205
src/parser.cc
205
src/parser.cc
@ -490,8 +490,6 @@ Parser::FunctionState::FunctionState(Parser* parser,
|
||||
: next_materialized_literal_index_(JSFunction::kLiteralsPrefixSize),
|
||||
next_handler_index_(0),
|
||||
expected_property_count_(0),
|
||||
only_simple_this_property_assignments_(false),
|
||||
this_property_assignments_(isolate->factory()->empty_fixed_array()),
|
||||
generator_object_variable_(NULL),
|
||||
parser_(parser),
|
||||
outer_function_state_(parser->current_function_state_),
|
||||
@ -675,8 +673,6 @@ FunctionLiteral* Parser::DoParseProgram(CompilationInfo* info,
|
||||
function_state.materialized_literal_count(),
|
||||
function_state.expected_property_count(),
|
||||
function_state.handler_count(),
|
||||
function_state.only_simple_this_property_assignments(),
|
||||
function_state.this_property_assignments(),
|
||||
0,
|
||||
FunctionLiteral::kNoDuplicateParameters,
|
||||
FunctionLiteral::ANONYMOUS_EXPRESSION,
|
||||
@ -850,178 +846,6 @@ void Parser::ReportMessageAt(Scanner::Location source_location,
|
||||
}
|
||||
|
||||
|
||||
// A ThisNamedPropertyAssignmentFinder finds and marks statements of the form
|
||||
// this.x = ...;, where x is a named property. It also determines whether a
|
||||
// function contains only assignments of this type.
|
||||
class ThisNamedPropertyAssignmentFinder {
|
||||
public:
|
||||
ThisNamedPropertyAssignmentFinder(Isolate* isolate, Zone* zone)
|
||||
: isolate_(isolate),
|
||||
only_simple_this_property_assignments_(true),
|
||||
names_(0, zone),
|
||||
assigned_arguments_(0, zone),
|
||||
assigned_constants_(0, zone),
|
||||
zone_(zone) {
|
||||
}
|
||||
|
||||
static Assignment* AsAssignment(Statement* stat) {
|
||||
if (stat == NULL) return NULL;
|
||||
ExpressionStatement* exp_stat = stat->AsExpressionStatement();
|
||||
if (exp_stat == NULL) return NULL;
|
||||
return exp_stat->expression()->AsAssignment();
|
||||
}
|
||||
|
||||
void Update(Scope* scope, Statement* stat) {
|
||||
// Bail out if function already has property assignment that are
|
||||
// not simple this property assignments.
|
||||
if (!only_simple_this_property_assignments_) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check whether this statement is of the form this.x = ...;
|
||||
Assignment* assignment = AsAssignment(stat);
|
||||
if (IsThisPropertyAssignment(assignment)) {
|
||||
HandleThisPropertyAssignment(scope, assignment);
|
||||
} else {
|
||||
only_simple_this_property_assignments_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns whether only statements of the form this.x = y; where y is either a
|
||||
// constant or a function argument was encountered.
|
||||
bool only_simple_this_property_assignments() {
|
||||
return only_simple_this_property_assignments_;
|
||||
}
|
||||
|
||||
// Returns a fixed array containing three elements for each assignment of the
|
||||
// form this.x = y;
|
||||
Handle<FixedArray> GetThisPropertyAssignments() {
|
||||
if (names_.is_empty()) {
|
||||
return isolate_->factory()->empty_fixed_array();
|
||||
}
|
||||
ASSERT_EQ(names_.length(), assigned_arguments_.length());
|
||||
ASSERT_EQ(names_.length(), assigned_constants_.length());
|
||||
Handle<FixedArray> assignments =
|
||||
isolate_->factory()->NewFixedArray(names_.length() * 3);
|
||||
for (int i = 0; i < names_.length(); ++i) {
|
||||
assignments->set(i * 3, *names_[i]);
|
||||
assignments->set(i * 3 + 1, Smi::FromInt(assigned_arguments_[i]));
|
||||
assignments->set(i * 3 + 2, *assigned_constants_[i]);
|
||||
}
|
||||
return assignments;
|
||||
}
|
||||
|
||||
private:
|
||||
bool IsThisPropertyAssignment(Assignment* assignment) {
|
||||
if (assignment != NULL) {
|
||||
Property* property = assignment->target()->AsProperty();
|
||||
return assignment->op() == Token::ASSIGN
|
||||
&& property != NULL
|
||||
&& property->obj()->AsVariableProxy() != NULL
|
||||
&& property->obj()->AsVariableProxy()->is_this();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void HandleThisPropertyAssignment(Scope* scope, Assignment* assignment) {
|
||||
// Check that the property assigned to is a named property, which is not
|
||||
// __proto__.
|
||||
Property* property = assignment->target()->AsProperty();
|
||||
ASSERT(property != NULL);
|
||||
Literal* literal = property->key()->AsLiteral();
|
||||
uint32_t dummy;
|
||||
if (literal != NULL &&
|
||||
literal->handle()->IsString() &&
|
||||
!String::cast(*(literal->handle()))->Equals(
|
||||
isolate_->heap()->proto_string()) &&
|
||||
!String::cast(*(literal->handle()))->AsArrayIndex(&dummy)) {
|
||||
Handle<String> key = Handle<String>::cast(literal->handle());
|
||||
|
||||
// Check whether the value assigned is either a constant or matches the
|
||||
// name of one of the arguments to the function.
|
||||
if (assignment->value()->AsLiteral() != NULL) {
|
||||
// Constant assigned.
|
||||
Literal* literal = assignment->value()->AsLiteral();
|
||||
AssignmentFromConstant(key, literal->handle());
|
||||
return;
|
||||
} else if (assignment->value()->AsVariableProxy() != NULL) {
|
||||
// Variable assigned.
|
||||
Handle<String> name =
|
||||
assignment->value()->AsVariableProxy()->name();
|
||||
// Check whether the variable assigned matches an argument name.
|
||||
for (int i = 0; i < scope->num_parameters(); i++) {
|
||||
if (*scope->parameter(i)->name() == *name) {
|
||||
// Assigned from function argument.
|
||||
AssignmentFromParameter(key, i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// It is not a simple "this.x = value;" assignment with a constant
|
||||
// or parameter value.
|
||||
AssignmentFromSomethingElse();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// We will potentially reorder the property assignments, so they must be
|
||||
// simple enough that the ordering does not matter.
|
||||
void AssignmentFromParameter(Handle<String> name, int index) {
|
||||
EnsureInitialized();
|
||||
for (int i = 0; i < names_.length(); ++i) {
|
||||
if (name->Equals(*names_[i])) {
|
||||
assigned_arguments_[i] = index;
|
||||
assigned_constants_[i] = isolate_->factory()->undefined_value();
|
||||
return;
|
||||
}
|
||||
}
|
||||
names_.Add(name, zone());
|
||||
assigned_arguments_.Add(index, zone());
|
||||
assigned_constants_.Add(isolate_->factory()->undefined_value(), zone());
|
||||
}
|
||||
|
||||
void AssignmentFromConstant(Handle<String> name, Handle<Object> value) {
|
||||
EnsureInitialized();
|
||||
for (int i = 0; i < names_.length(); ++i) {
|
||||
if (name->Equals(*names_[i])) {
|
||||
assigned_arguments_[i] = -1;
|
||||
assigned_constants_[i] = value;
|
||||
return;
|
||||
}
|
||||
}
|
||||
names_.Add(name, zone());
|
||||
assigned_arguments_.Add(-1, zone());
|
||||
assigned_constants_.Add(value, zone());
|
||||
}
|
||||
|
||||
void AssignmentFromSomethingElse() {
|
||||
// The this assignment is not a simple one.
|
||||
only_simple_this_property_assignments_ = false;
|
||||
}
|
||||
|
||||
void EnsureInitialized() {
|
||||
if (names_.capacity() == 0) {
|
||||
ASSERT(assigned_arguments_.capacity() == 0);
|
||||
ASSERT(assigned_constants_.capacity() == 0);
|
||||
names_.Initialize(4, zone());
|
||||
assigned_arguments_.Initialize(4, zone());
|
||||
assigned_constants_.Initialize(4, zone());
|
||||
}
|
||||
}
|
||||
|
||||
Zone* zone() const { return zone_; }
|
||||
|
||||
Isolate* isolate_;
|
||||
bool only_simple_this_property_assignments_;
|
||||
ZoneStringList names_;
|
||||
ZoneList<int> assigned_arguments_;
|
||||
ZoneObjectList assigned_constants_;
|
||||
Zone* zone_;
|
||||
};
|
||||
|
||||
|
||||
void* Parser::ParseSourceElements(ZoneList<Statement*>* processor,
|
||||
int end_token,
|
||||
bool is_eval,
|
||||
@ -1037,8 +861,6 @@ void* Parser::ParseSourceElements(ZoneList<Statement*>* processor,
|
||||
TargetScope scope(&this->target_stack_);
|
||||
|
||||
ASSERT(processor != NULL);
|
||||
ThisNamedPropertyAssignmentFinder this_property_assignment_finder(isolate(),
|
||||
zone());
|
||||
bool directive_prologue = true; // Parsing directive prologue.
|
||||
|
||||
while (peek() != end_token) {
|
||||
@ -1098,25 +920,9 @@ void* Parser::ParseSourceElements(ZoneList<Statement*>* processor,
|
||||
}
|
||||
}
|
||||
|
||||
// Find and mark all assignments to named properties in this (this.x =)
|
||||
if (top_scope_->is_function_scope()) {
|
||||
this_property_assignment_finder.Update(top_scope_, stat);
|
||||
}
|
||||
processor->Add(stat, zone());
|
||||
}
|
||||
|
||||
// Propagate the collected information on this property assignments.
|
||||
if (top_scope_->is_function_scope()) {
|
||||
bool only_simple_this_property_assignments =
|
||||
this_property_assignment_finder.only_simple_this_property_assignments()
|
||||
&& top_scope_->declarations()->length() == 0;
|
||||
if (only_simple_this_property_assignments) {
|
||||
current_function_state_->SetThisPropertyAssignmentInfo(
|
||||
only_simple_this_property_assignments,
|
||||
this_property_assignment_finder.GetThisPropertyAssignments());
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -4387,8 +4193,6 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String> function_name,
|
||||
int materialized_literal_count = -1;
|
||||
int expected_property_count = -1;
|
||||
int handler_count = 0;
|
||||
bool only_simple_this_property_assignments;
|
||||
Handle<FixedArray> this_property_assignments;
|
||||
FunctionLiteral::ParameterFlag duplicate_parameters =
|
||||
FunctionLiteral::kNoDuplicateParameters;
|
||||
FunctionLiteral::IsParenthesizedFlag parenthesized = parenthesized_function_
|
||||
@ -4519,8 +4323,6 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String> function_name,
|
||||
materialized_literal_count = entry.literal_count();
|
||||
expected_property_count = entry.property_count();
|
||||
top_scope_->SetLanguageMode(entry.language_mode());
|
||||
only_simple_this_property_assignments = false;
|
||||
this_property_assignments = isolate()->factory()->empty_fixed_array();
|
||||
} else {
|
||||
is_lazily_compiled = false;
|
||||
}
|
||||
@ -4555,8 +4357,6 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String> function_name,
|
||||
materialized_literal_count = logger.literals();
|
||||
expected_property_count = logger.properties();
|
||||
top_scope_->SetLanguageMode(logger.language_mode());
|
||||
only_simple_this_property_assignments = false;
|
||||
this_property_assignments = isolate()->factory()->empty_fixed_array();
|
||||
}
|
||||
}
|
||||
|
||||
@ -4609,9 +4409,6 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String> function_name,
|
||||
materialized_literal_count = function_state.materialized_literal_count();
|
||||
expected_property_count = function_state.expected_property_count();
|
||||
handler_count = function_state.handler_count();
|
||||
only_simple_this_property_assignments =
|
||||
function_state.only_simple_this_property_assignments();
|
||||
this_property_assignments = function_state.this_property_assignments();
|
||||
|
||||
Expect(Token::RBRACE, CHECK_OK);
|
||||
scope->set_end_position(scanner().location().end_pos);
|
||||
@ -4677,8 +4474,6 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String> function_name,
|
||||
materialized_literal_count,
|
||||
expected_property_count,
|
||||
handler_count,
|
||||
only_simple_this_property_assignments,
|
||||
this_property_assignments,
|
||||
num_parameters,
|
||||
duplicate_parameters,
|
||||
type,
|
||||
|
19
src/parser.h
19
src/parser.h
@ -501,20 +501,6 @@ class Parser BASE_EMBEDDED {
|
||||
int NextHandlerIndex() { return next_handler_index_++; }
|
||||
int handler_count() { return next_handler_index_; }
|
||||
|
||||
void SetThisPropertyAssignmentInfo(
|
||||
bool only_simple_this_property_assignments,
|
||||
Handle<FixedArray> this_property_assignments) {
|
||||
only_simple_this_property_assignments_ =
|
||||
only_simple_this_property_assignments;
|
||||
this_property_assignments_ = this_property_assignments;
|
||||
}
|
||||
bool only_simple_this_property_assignments() {
|
||||
return only_simple_this_property_assignments_;
|
||||
}
|
||||
Handle<FixedArray> this_property_assignments() {
|
||||
return this_property_assignments_;
|
||||
}
|
||||
|
||||
void AddProperty() { expected_property_count_++; }
|
||||
int expected_property_count() { return expected_property_count_; }
|
||||
|
||||
@ -544,11 +530,6 @@ class Parser BASE_EMBEDDED {
|
||||
// Properties count estimation.
|
||||
int expected_property_count_;
|
||||
|
||||
// Keeps track of assignments to properties of this. Used for
|
||||
// optimizing constructors.
|
||||
bool only_simple_this_property_assignments_;
|
||||
Handle<FixedArray> this_property_assignments_;
|
||||
|
||||
// For generators, the variable that holds the generator object. This
|
||||
// variable is used by yield expressions and return statements. NULL
|
||||
// indicates that this function is not a generator.
|
||||
|
@ -2513,10 +2513,6 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_SetCode) {
|
||||
// Since we don't store the source we should never optimize this.
|
||||
target_shared->code()->set_optimizable(false);
|
||||
|
||||
// Clear the optimization hints related to the compiled code as these
|
||||
// are no longer valid when the code is overwritten.
|
||||
target_shared->ClearThisPropertyAssignmentsInfo();
|
||||
|
||||
// Set the code of the target function.
|
||||
target->ReplaceCode(source_shared->code());
|
||||
ASSERT(target->next_function_link()->IsUndefined());
|
||||
|
@ -2029,15 +2029,6 @@ Handle<Code> CallStubCompiler::GetCode(Handle<JSFunction> function) {
|
||||
}
|
||||
|
||||
|
||||
Handle<Code> ConstructStubCompiler::GetCode() {
|
||||
Code::Flags flags = Code::ComputeFlags(Code::STUB);
|
||||
Handle<Code> code = GetCodeWithFlags(flags, "ConstructStub");
|
||||
PROFILE(isolate(), CodeCreateEvent(Logger::STUB_TAG, *code, "ConstructStub"));
|
||||
GDBJIT(AddCode(GDBJITInterface::STUB, "ConstructStub", *code));
|
||||
return code;
|
||||
}
|
||||
|
||||
|
||||
CallOptimization::CallOptimization(LookupResult* lookup) {
|
||||
if (lookup->IsFound() &&
|
||||
lookup->IsCacheable() &&
|
||||
|
@ -1067,17 +1067,6 @@ class CallStubCompiler: public StubCompiler {
|
||||
};
|
||||
|
||||
|
||||
class ConstructStubCompiler: public StubCompiler {
|
||||
public:
|
||||
explicit ConstructStubCompiler(Isolate* isolate) : StubCompiler(isolate) { }
|
||||
|
||||
Handle<Code> CompileConstructStub(Handle<JSFunction> function);
|
||||
|
||||
private:
|
||||
Handle<Code> GetCode();
|
||||
};
|
||||
|
||||
|
||||
// Holds information about possible function call optimizations.
|
||||
class CallOptimization BASE_EMBEDDED {
|
||||
public:
|
||||
|
@ -2961,139 +2961,6 @@ Handle<Code> BaseLoadStubCompiler::CompilePolymorphicIC(
|
||||
}
|
||||
|
||||
|
||||
// Specialized stub for constructing objects from functions which only have only
|
||||
// simple assignments of the form this.x = ...; in their body.
|
||||
Handle<Code> ConstructStubCompiler::CompileConstructStub(
|
||||
Handle<JSFunction> function) {
|
||||
// ----------- S t a t e -------------
|
||||
// -- rax : argc
|
||||
// -- rdi : constructor
|
||||
// -- rsp[0] : return address
|
||||
// -- rsp[4] : last argument
|
||||
// -----------------------------------
|
||||
Label generic_stub_call;
|
||||
|
||||
// Use r8 for holding undefined which is used in several places below.
|
||||
__ Move(r8, factory()->undefined_value());
|
||||
|
||||
#ifdef ENABLE_DEBUGGER_SUPPORT
|
||||
// Check to see whether there are any break points in the function code. If
|
||||
// there are jump to the generic constructor stub which calls the actual
|
||||
// code for the function thereby hitting the break points.
|
||||
__ movq(rbx, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ movq(rbx, FieldOperand(rbx, SharedFunctionInfo::kDebugInfoOffset));
|
||||
__ cmpq(rbx, r8);
|
||||
__ j(not_equal, &generic_stub_call);
|
||||
#endif
|
||||
|
||||
// Load the initial map and verify that it is in fact a map.
|
||||
// rdi: constructor
|
||||
__ movq(rbx, FieldOperand(rdi, JSFunction::kPrototypeOrInitialMapOffset));
|
||||
// Will both indicate a NULL and a Smi.
|
||||
STATIC_ASSERT(kSmiTag == 0);
|
||||
__ JumpIfSmi(rbx, &generic_stub_call);
|
||||
__ CmpObjectType(rbx, MAP_TYPE, rcx);
|
||||
__ j(not_equal, &generic_stub_call);
|
||||
|
||||
#ifdef DEBUG
|
||||
// Cannot construct functions this way.
|
||||
// rbx: initial map
|
||||
__ CmpInstanceType(rbx, JS_FUNCTION_TYPE);
|
||||
__ Check(not_equal, "Function constructed by construct stub.");
|
||||
#endif
|
||||
|
||||
// Now allocate the JSObject in new space.
|
||||
// rbx: initial map
|
||||
ASSERT(function->has_initial_map());
|
||||
int instance_size = function->initial_map()->instance_size();
|
||||
#ifdef DEBUG
|
||||
__ movzxbq(rcx, FieldOperand(rbx, Map::kInstanceSizeOffset));
|
||||
__ shl(rcx, Immediate(kPointerSizeLog2));
|
||||
__ cmpq(rcx, Immediate(instance_size));
|
||||
__ Check(equal, "Instance size of initial map changed.");
|
||||
#endif
|
||||
__ Allocate(instance_size, rdx, rcx, no_reg, &generic_stub_call,
|
||||
NO_ALLOCATION_FLAGS);
|
||||
|
||||
// Allocated the JSObject, now initialize the fields and add the heap tag.
|
||||
// rbx: initial map
|
||||
// rdx: JSObject (untagged)
|
||||
__ movq(Operand(rdx, JSObject::kMapOffset), rbx);
|
||||
__ Move(rbx, factory()->empty_fixed_array());
|
||||
__ movq(Operand(rdx, JSObject::kPropertiesOffset), rbx);
|
||||
__ movq(Operand(rdx, JSObject::kElementsOffset), rbx);
|
||||
|
||||
// rax: argc
|
||||
// rdx: JSObject (untagged)
|
||||
// Load the address of the first in-object property into r9.
|
||||
__ lea(r9, Operand(rdx, JSObject::kHeaderSize));
|
||||
// Calculate the location of the first argument. The stack contains only the
|
||||
// return address on top of the argc arguments.
|
||||
__ lea(rcx, Operand(rsp, rax, times_pointer_size, 0));
|
||||
|
||||
// rax: argc
|
||||
// rcx: first argument
|
||||
// rdx: JSObject (untagged)
|
||||
// r8: undefined
|
||||
// r9: first in-object property of the JSObject
|
||||
// Fill the initialized properties with a constant value or a passed argument
|
||||
// depending on the this.x = ...; assignment in the function.
|
||||
Handle<SharedFunctionInfo> shared(function->shared());
|
||||
for (int i = 0; i < shared->this_property_assignments_count(); i++) {
|
||||
if (shared->IsThisPropertyAssignmentArgument(i)) {
|
||||
// Check if the argument assigned to the property is actually passed.
|
||||
// If argument is not passed the property is set to undefined,
|
||||
// otherwise find it on the stack.
|
||||
int arg_number = shared->GetThisPropertyAssignmentArgument(i);
|
||||
__ movq(rbx, r8);
|
||||
__ cmpq(rax, Immediate(arg_number));
|
||||
__ cmovq(above, rbx, Operand(rcx, arg_number * -kPointerSize));
|
||||
// Store value in the property.
|
||||
__ movq(Operand(r9, i * kPointerSize), rbx);
|
||||
} else {
|
||||
// Set the property to the constant value.
|
||||
Handle<Object> constant(shared->GetThisPropertyAssignmentConstant(i),
|
||||
isolate());
|
||||
__ Move(Operand(r9, i * kPointerSize), constant);
|
||||
}
|
||||
}
|
||||
|
||||
// Fill the unused in-object property fields with undefined.
|
||||
for (int i = shared->this_property_assignments_count();
|
||||
i < function->initial_map()->inobject_properties();
|
||||
i++) {
|
||||
__ movq(Operand(r9, i * kPointerSize), r8);
|
||||
}
|
||||
|
||||
// rax: argc
|
||||
// rdx: JSObject (untagged)
|
||||
// Move argc to rbx and the JSObject to return to rax and tag it.
|
||||
__ movq(rbx, rax);
|
||||
__ movq(rax, rdx);
|
||||
__ or_(rax, Immediate(kHeapObjectTag));
|
||||
|
||||
// rax: JSObject
|
||||
// rbx: argc
|
||||
// Remove caller arguments and receiver from the stack and return.
|
||||
__ pop(rcx);
|
||||
__ lea(rsp, Operand(rsp, rbx, times_pointer_size, 1 * kPointerSize));
|
||||
__ push(rcx);
|
||||
Counters* counters = isolate()->counters();
|
||||
__ IncrementCounter(counters->constructed_objects(), 1);
|
||||
__ IncrementCounter(counters->constructed_objects_stub(), 1);
|
||||
__ ret(0);
|
||||
|
||||
// Jump to the generic stub in case the specialized code cannot handle the
|
||||
// construction.
|
||||
__ bind(&generic_stub_call);
|
||||
Handle<Code> code = isolate()->builtins()->JSConstructStubGeneric();
|
||||
__ Jump(code, RelocInfo::CODE_TARGET);
|
||||
|
||||
// Return the generated code.
|
||||
return GetCode();
|
||||
}
|
||||
|
||||
|
||||
#undef __
|
||||
#define __ ACCESS_MASM(masm)
|
||||
|
||||
|
@ -1,45 +0,0 @@
|
||||
// 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.
|
||||
|
||||
// When this bug is corrected move to object-define-property and add
|
||||
// additional tests for configurable in the same manner as existing tests
|
||||
// there.
|
||||
|
||||
function C() {
|
||||
this.x = 23;
|
||||
}
|
||||
|
||||
// If a setter is added to the prototype chain of a simple constructor setting
|
||||
// one of the properties assigned in the constructor then this setter is
|
||||
// ignored when constructing new objects from the constructor.
|
||||
|
||||
// This only happens if the setter is added _after_ an instance has been
|
||||
// created.
|
||||
|
||||
assertEquals(23, new C().x);
|
||||
C.prototype.__defineSetter__('x', function(value) { this.y = 23; });
|
||||
assertEquals(void 0, new C().x);
|
@ -1195,3 +1195,12 @@ Assign(new C);
|
||||
%OptimizeFunctionOnNextCall(Assign);
|
||||
Object.defineProperty(C.prototype, "blubb", {get: function() { return -42; }});
|
||||
Assign(new C);
|
||||
|
||||
// Test that changes to the prototype of a simple constructor are not ignored,
|
||||
// even after creating initial instances.
|
||||
function C() {
|
||||
this.x = 23;
|
||||
}
|
||||
assertEquals(23, new C().x);
|
||||
C.prototype.__defineSetter__('x', function(value) { this.y = 23; });
|
||||
assertEquals(void 0, new C().x);
|
||||
|
Loading…
Reference in New Issue
Block a user