From: vegorov@chromium.org Date: Mon, 12 Mar 2012 12:49:41 +0000 (+0000) Subject: Inline functions that use arguments object in f.apply(o, arguments) pattern. X-Git-Tag: upstream/4.7.83~17124 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=da03f56b1fbdb10f61a657819ee6e6ccdd95d2f8;p=platform%2Fupstream%2Fv8.git Inline functions that use arguments object in f.apply(o, arguments) pattern. Support arguments materialization after deoptimization in all frames (not only in topmost one). R=fschneider@chromium.org Review URL: https://chromiumcodereview.appspot.com/9643001 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@11008 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- diff --git a/src/arm/lithium-arm.cc b/src/arm/lithium-arm.cc index 36421d9..cdc1947 100644 --- a/src/arm/lithium-arm.cc +++ b/src/arm/lithium-arm.cc @@ -1098,6 +1098,14 @@ LInstruction* LChunkBuilder::DoInstanceOfKnownGlobal( } +LInstruction* LChunkBuilder::DoWrapReceiver(HWrapReceiver* instr) { + LOperand* receiver = UseRegisterAtStart(instr->receiver()); + LOperand* function = UseRegisterAtStart(instr->function()); + LWrapReceiver* result = new(zone()) LWrapReceiver(receiver, function); + return AssignEnvironment(DefineSameAsFirst(result)); +} + + LInstruction* LChunkBuilder::DoApplyArguments(HApplyArguments* instr) { LOperand* function = UseFixed(instr->function(), r1); LOperand* receiver = UseFixed(instr->receiver(), r0); diff --git a/src/arm/lithium-arm.h b/src/arm/lithium-arm.h index ae19677..62cde6e 100644 --- a/src/arm/lithium-arm.h +++ b/src/arm/lithium-arm.h @@ -178,7 +178,8 @@ class LCodeGen; V(ForInCacheArray) \ V(CheckMapValue) \ V(LoadFieldByIndex) \ - V(DateField) + V(DateField) \ + V(WrapReceiver) #define DECLARE_CONCRETE_INSTRUCTION(type, mnemonic) \ @@ -468,6 +469,20 @@ class LControlInstruction: public LTemplateInstruction<0, I, T> { }; +class LWrapReceiver: public LTemplateInstruction<1, 2, 0> { + public: + LWrapReceiver(LOperand* receiver, LOperand* function) { + inputs_[0] = receiver; + inputs_[1] = function; + } + + DECLARE_CONCRETE_INSTRUCTION(WrapReceiver, "wrap-receiver") + + LOperand* receiver() { return inputs_[0]; } + LOperand* function() { return inputs_[1]; } +}; + + class LApplyArguments: public LTemplateInstruction<1, 4, 0> { public: LApplyArguments(LOperand* function, diff --git a/src/arm/lithium-codegen-arm.cc b/src/arm/lithium-codegen-arm.cc index 012ea45..66b2c58 100644 --- a/src/arm/lithium-codegen-arm.cc +++ b/src/arm/lithium-codegen-arm.cc @@ -2800,15 +2800,10 @@ void LCodeGen::DoArgumentsLength(LArgumentsLength* instr) { } -void LCodeGen::DoApplyArguments(LApplyArguments* instr) { +void LCodeGen::DoWrapReceiver(LWrapReceiver* instr) { Register receiver = ToRegister(instr->receiver()); Register function = ToRegister(instr->function()); - Register length = ToRegister(instr->length()); - Register elements = ToRegister(instr->elements()); Register scratch = scratch0(); - ASSERT(receiver.is(r0)); // Used for parameter count. - ASSERT(function.is(r1)); // Required by InvokeFunction. - ASSERT(ToRegister(instr->result()).is(r0)); // If the receiver is null or undefined, we have to pass the global // object as a receiver to normal functions. Values have to be @@ -2849,6 +2844,18 @@ void LCodeGen::DoApplyArguments(LApplyArguments* instr) { __ ldr(receiver, FieldMemOperand(receiver, JSGlobalObject::kGlobalReceiverOffset)); __ bind(&receiver_ok); +} + + +void LCodeGen::DoApplyArguments(LApplyArguments* instr) { + Register receiver = ToRegister(instr->receiver()); + Register function = ToRegister(instr->function()); + Register length = ToRegister(instr->length()); + Register elements = ToRegister(instr->elements()); + Register scratch = scratch0(); + ASSERT(receiver.is(r0)); // Used for parameter count. + ASSERT(function.is(r1)); // Required by InvokeFunction. + ASSERT(ToRegister(instr->result()).is(r0)); // Copy the arguments to this function possibly from the // adaptor frame below it. diff --git a/src/deoptimizer.cc b/src/deoptimizer.cc index d069a45..d18c1a6 100644 --- a/src/deoptimizer.cc +++ b/src/deoptimizer.cc @@ -847,7 +847,6 @@ void Deoptimizer::DoTranslateCommand(TranslationIterator* iterator, case Translation::ARGUMENTS_OBJECT: { // Use the arguments marker value as a sentinel and fill in the arguments // object after the deoptimized frame is built. - ASSERT(frame_index == 0); // Only supported for first frame. if (FLAG_trace_deopt) { PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- ", output_[frame_index]->GetTop() + output_offset, diff --git a/src/flag-definitions.h b/src/flag-definitions.h index ac30b29..fe25a55 100644 --- a/src/flag-definitions.h +++ b/src/flag-definitions.h @@ -169,6 +169,7 @@ DEFINE_bool(trace_osr, false, "trace on-stack replacement") DEFINE_int(stress_runs, 0, "number of stress runs") DEFINE_bool(optimize_closures, true, "optimize closures") DEFINE_bool(inline_construct, false, "inline constructor calls") +DEFINE_bool(inline_arguments, true, "inline functions with arguments object") DEFINE_int(loop_weight, 1, "loop weight for representation inference") DEFINE_bool(optimize_for_in, true, diff --git a/src/hydrogen-instructions.cc b/src/hydrogen-instructions.cc index f7391dd..6db297b 100644 --- a/src/hydrogen-instructions.cc +++ b/src/hydrogen-instructions.cc @@ -885,6 +885,15 @@ HValue* HChange::Canonicalize() { } +HValue* HWrapReceiver::Canonicalize() { + if (HasNoUses()) return NULL; + if (receiver()->type().IsJSObject()) { + return receiver(); + } + return this; +} + + void HTypeof::PrintDataTo(StringStream* stream) { value()->PrintNameTo(stream); } diff --git a/src/hydrogen-instructions.h b/src/hydrogen-instructions.h index 893c708..53f0de1 100644 --- a/src/hydrogen-instructions.h +++ b/src/hydrogen-instructions.h @@ -185,7 +185,8 @@ class LChunkBuilder; V(ForInCacheArray) \ V(CheckMapValue) \ V(LoadFieldByIndex) \ - V(DateField) + V(DateField) \ + V(WrapReceiver) #define GVN_FLAG_LIST(V) \ V(Calls) \ @@ -2503,6 +2504,27 @@ class HBinaryOperation: public HTemplateInstruction<3> { }; +class HWrapReceiver: public HTemplateInstruction<2> { + public: + HWrapReceiver(HValue* receiver, HValue* function) { + set_representation(Representation::Tagged()); + SetOperandAt(0, receiver); + SetOperandAt(1, function); + } + + virtual Representation RequiredInputRepresentation(int index) { + return Representation::Tagged(); + } + + HValue* receiver() { return OperandAt(0); } + HValue* function() { return OperandAt(1); } + + virtual HValue* Canonicalize(); + + DECLARE_CONCRETE_INSTRUCTION(WrapReceiver) +}; + + class HApplyArguments: public HTemplateInstruction<4> { public: HApplyArguments(HValue* function, diff --git a/src/hydrogen.cc b/src/hydrogen.cc index 11b1157..6a7b609 100644 --- a/src/hydrogen.cc +++ b/src/hydrogen.cc @@ -2639,10 +2639,14 @@ void HGraphBuilder::SetUpScope(Scope* scope) { if (!scope->arguments()->IsStackAllocated()) { return Bailout("context-allocated arguments"); } - HArgumentsObject* object = new(zone()) HArgumentsObject; - AddInstruction(object); - graph()->SetArgumentsObject(object); - environment()->Bind(scope->arguments(), object); + + if (!graph()->HasArgumentsObject()) { + HArgumentsObject* object = new(zone()) HArgumentsObject; + AddInstruction(object); + graph()->SetArgumentsObject(object); + } + environment()->Bind(scope->arguments(), + graph()->GetArgumentsObject()); } } @@ -5226,10 +5230,21 @@ bool HGraphBuilder::TryInline(CallKind call_kind, return false; } - // Don't inline functions that uses the arguments object. + // If the function uses the arguments object check that inlining of functions + // with arguments object is enabled and the arguments-variable is + // stack allocated. if (function->scope()->arguments() != NULL) { - TraceInline(target, caller, "target requires special argument handling"); - return false; + if (!FLAG_inline_arguments) { + TraceInline(target, caller, "target uses arguments object"); + return false; + } + + if (!function->scope()->arguments()->IsStackAllocated()) { + TraceInline(target, + caller, + "target uses non-stackallocated arguments object"); + return false; + } } // All declarations must be inlineable. @@ -5307,6 +5322,17 @@ bool HGraphBuilder::TryInline(CallKind call_kind, function, call_kind, function_state()->is_construct())); + // If the function uses arguments object create and bind one. + if (function->scope()->arguments() != NULL) { + ASSERT(function->scope()->arguments()->IsStackAllocated()); + if (!graph()->HasArgumentsObject()) { + HArgumentsObject* object = new(zone()) HArgumentsObject; + AddInstruction(object); + graph()->SetArgumentsObject(object); + } + environment()->Bind(function->scope()->arguments(), + graph()->GetArgumentsObject()); + } VisitDeclarations(target_info.scope()->declarations()); VisitStatements(function->body()); if (HasStackOverflow()) { @@ -5645,13 +5671,6 @@ bool HGraphBuilder::TryCallApply(Call* expr) { HValue* arg_two_value = environment()->Lookup(arg_two->var()); if (!arg_two_value->CheckFlag(HValue::kIsArguments)) return false; - // Our implementation of arguments (based on this stack frame or an - // adapter below it) does not work for inlined functions. - if (function_state()->outer() != NULL) { - Bailout("Function.prototype.apply optimization in inlined function"); - return true; - } - // Found pattern f.apply(receiver, arguments). VisitForValue(prop->obj()); if (HasStackOverflow() || current_block() == NULL) return true; @@ -5662,13 +5681,55 @@ bool HGraphBuilder::TryCallApply(Call* expr) { VisitForValue(args->at(0)); if (HasStackOverflow() || current_block() == NULL) return true; HValue* receiver = Pop(); - HInstruction* elements = AddInstruction(new(zone()) HArgumentsElements); - HInstruction* length = AddInstruction(new(zone()) HArgumentsLength(elements)); - HInstruction* result = - new(zone()) HApplyArguments(function, receiver, length, elements); - result->set_position(expr->position()); - ast_context()->ReturnInstruction(result, expr->id()); - return true; + + if (function_state()->outer() == NULL) { + HInstruction* elements = AddInstruction(new(zone()) HArgumentsElements); + HInstruction* length = + AddInstruction(new(zone()) HArgumentsLength(elements)); + HValue* wrapped_receiver = + AddInstruction(new(zone()) HWrapReceiver(receiver, function)); + HInstruction* result = + new(zone()) HApplyArguments(function, + wrapped_receiver, + length, + elements); + result->set_position(expr->position()); + ast_context()->ReturnInstruction(result, expr->id()); + return true; + } else { + // We are inside inlined function and we know exactly what is inside + // arguments object. + HValue* context = environment()->LookupContext(); + + HValue* wrapped_receiver = + AddInstruction(new(zone()) HWrapReceiver(receiver, function)); + PushAndAdd(new(zone()) HPushArgument(wrapped_receiver)); + + int parameter_count = environment()->parameter_count(); + for (int i = 1; i < environment()->parameter_count(); i++) { + PushAndAdd(new(zone()) HPushArgument(environment()->Lookup(i))); + } + + if (environment()->outer()->frame_type() == ARGUMENTS_ADAPTOR) { + HEnvironment* adaptor = environment()->outer(); + parameter_count = adaptor->parameter_count(); + + for (int i = environment()->parameter_count(); + i < adaptor->parameter_count(); + i++) { + PushAndAdd(new(zone()) HPushArgument(adaptor->Lookup(i))); + } + } + + HInvokeFunction* call = new(zone()) HInvokeFunction( + context, + function, + parameter_count); + Drop(parameter_count); + call->set_position(expr->position()); + ast_context()->ReturnInstruction(call, expr->id()); + return true; + } } diff --git a/src/ia32/lithium-codegen-ia32.cc b/src/ia32/lithium-codegen-ia32.cc index a656175..04008ee 100644 --- a/src/ia32/lithium-codegen-ia32.cc +++ b/src/ia32/lithium-codegen-ia32.cc @@ -2625,15 +2625,10 @@ void LCodeGen::DoArgumentsLength(LArgumentsLength* instr) { } -void LCodeGen::DoApplyArguments(LApplyArguments* instr) { +void LCodeGen::DoWrapReceiver(LWrapReceiver* instr) { Register receiver = ToRegister(instr->receiver()); Register function = ToRegister(instr->function()); - Register length = ToRegister(instr->length()); - Register elements = ToRegister(instr->elements()); Register scratch = ToRegister(instr->TempAt(0)); - ASSERT(receiver.is(eax)); // Used for parameter count. - ASSERT(function.is(edi)); // Required by InvokeFunction. - ASSERT(ToRegister(instr->result()).is(eax)); // If the receiver is null or undefined, we have to pass the global // object as a receiver to normal functions. Values have to be @@ -2675,6 +2670,17 @@ void LCodeGen::DoApplyArguments(LApplyArguments* instr) { __ mov(receiver, FieldOperand(receiver, JSGlobalObject::kGlobalReceiverOffset)); __ bind(&receiver_ok); +} + + +void LCodeGen::DoApplyArguments(LApplyArguments* instr) { + Register receiver = ToRegister(instr->receiver()); + Register function = ToRegister(instr->function()); + Register length = ToRegister(instr->length()); + Register elements = ToRegister(instr->elements()); + ASSERT(receiver.is(eax)); // Used for parameter count. + ASSERT(function.is(edi)); // Required by InvokeFunction. + ASSERT(ToRegister(instr->result()).is(eax)); // Copy the arguments to this function possibly from the // adaptor frame below it. diff --git a/src/ia32/lithium-ia32.cc b/src/ia32/lithium-ia32.cc index 223fde2..18e4645 100644 --- a/src/ia32/lithium-ia32.cc +++ b/src/ia32/lithium-ia32.cc @@ -1111,17 +1111,25 @@ LInstruction* LChunkBuilder::DoInstanceOfKnownGlobal( } +LInstruction* LChunkBuilder::DoWrapReceiver(HWrapReceiver* instr) { + LOperand* receiver = UseRegister(instr->receiver()); + LOperand* function = UseRegisterAtStart(instr->function()); + LOperand* temp = TempRegister(); + LWrapReceiver* result = + new(zone()) LWrapReceiver(receiver, function, temp); + return AssignEnvironment(DefineSameAsFirst(result)); +} + + LInstruction* LChunkBuilder::DoApplyArguments(HApplyArguments* instr) { LOperand* function = UseFixed(instr->function(), edi); LOperand* receiver = UseFixed(instr->receiver(), eax); LOperand* length = UseFixed(instr->length(), ebx); LOperand* elements = UseFixed(instr->elements(), ecx); - LOperand* temp = FixedTemp(edx); LApplyArguments* result = new(zone()) LApplyArguments(function, receiver, length, - elements, - temp); + elements); return MarkAsCall(DefineFixed(result, eax), instr, CAN_DEOPTIMIZE_EAGERLY); } diff --git a/src/ia32/lithium-ia32.h b/src/ia32/lithium-ia32.h index dd41bfb..778bd68 100644 --- a/src/ia32/lithium-ia32.h +++ b/src/ia32/lithium-ia32.h @@ -173,7 +173,8 @@ class LCodeGen; V(ForInCacheArray) \ V(CheckMapValue) \ V(LoadFieldByIndex) \ - V(DateField) + V(DateField) \ + V(WrapReceiver) #define DECLARE_CONCRETE_INSTRUCTION(type, mnemonic) \ @@ -456,18 +457,33 @@ class LControlInstruction: public LTemplateInstruction<0, I, T> { }; -class LApplyArguments: public LTemplateInstruction<1, 4, 1> { +class LWrapReceiver: public LTemplateInstruction<1, 2, 1> { + public: + LWrapReceiver(LOperand* receiver, + LOperand* function, + LOperand* temp) { + inputs_[0] = receiver; + inputs_[1] = function; + temps_[0] = temp; + } + + DECLARE_CONCRETE_INSTRUCTION(WrapReceiver, "wrap-receiver") + + LOperand* receiver() { return inputs_[0]; } + LOperand* function() { return inputs_[1]; } +}; + + +class LApplyArguments: public LTemplateInstruction<1, 4, 0> { public: LApplyArguments(LOperand* function, LOperand* receiver, LOperand* length, - LOperand* elements, - LOperand* temp) { + LOperand* elements) { inputs_[0] = function; inputs_[1] = receiver; inputs_[2] = length; inputs_[3] = elements; - temps_[0] = temp; } DECLARE_CONCRETE_INSTRUCTION(ApplyArguments, "apply-arguments") diff --git a/src/runtime.cc b/src/runtime.cc index cc5aeab..2a8e54f 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -8112,6 +8112,27 @@ class ActivationsFinder : public ThreadVisitor { }; +static void MaterializeArgumentsObjectInFrame(Isolate* isolate, + JavaScriptFrame* frame) { + Handle function(JSFunction::cast(frame->function()), isolate); + Handle arguments; + for (int i = frame->ComputeExpressionsCount() - 1; i >= 0; --i) { + if (frame->GetExpression(i) == isolate->heap()->arguments_marker()) { + if (arguments.is_null()) { + // FunctionGetArguments can't throw an exception, so cast away the + // doubt with an assert. + arguments = Handle( + Accessors::FunctionGetArguments(*function, + NULL)->ToObjectUnchecked()); + ASSERT(*arguments != isolate->heap()->null_value()); + ASSERT(*arguments != isolate->heap()->undefined_value()); + } + frame->SetExpression(i, *arguments); + } + } +} + + RUNTIME_FUNCTION(MaybeObject*, Runtime_NotifyDeoptimized) { HandleScope scope(isolate); ASSERT(args.length() == 1); @@ -8126,27 +8147,15 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_NotifyDeoptimized) { delete deoptimizer; JavaScriptFrameIterator it(isolate); - JavaScriptFrame* frame = NULL; - for (int i = 0; i < jsframes - 1; i++) it.Advance(); - frame = it.frame(); + for (int i = 0; i < jsframes - 1; i++) { + MaterializeArgumentsObjectInFrame(isolate, it.frame()); + it.Advance(); + } + JavaScriptFrame* frame = it.frame(); RUNTIME_ASSERT(frame->function()->IsJSFunction()); Handle function(JSFunction::cast(frame->function()), isolate); - Handle arguments; - for (int i = frame->ComputeExpressionsCount() - 1; i >= 0; --i) { - if (frame->GetExpression(i) == isolate->heap()->arguments_marker()) { - if (arguments.is_null()) { - // FunctionGetArguments can't throw an exception, so cast away the - // doubt with an assert. - arguments = Handle( - Accessors::FunctionGetArguments(*function, - NULL)->ToObjectUnchecked()); - ASSERT(*arguments != isolate->heap()->null_value()); - ASSERT(*arguments != isolate->heap()->undefined_value()); - } - frame->SetExpression(i, *arguments); - } - } + MaterializeArgumentsObjectInFrame(isolate, frame); if (type == Deoptimizer::EAGER) { RUNTIME_ASSERT(function->IsOptimized()); diff --git a/src/x64/lithium-codegen-x64.cc b/src/x64/lithium-codegen-x64.cc index f707df0..20b9748 100644 --- a/src/x64/lithium-codegen-x64.cc +++ b/src/x64/lithium-codegen-x64.cc @@ -2544,14 +2544,9 @@ void LCodeGen::DoArgumentsLength(LArgumentsLength* instr) { } -void LCodeGen::DoApplyArguments(LApplyArguments* instr) { +void LCodeGen::DoWrapReceiver(LWrapReceiver* instr) { Register receiver = ToRegister(instr->receiver()); Register function = ToRegister(instr->function()); - Register length = ToRegister(instr->length()); - Register elements = ToRegister(instr->elements()); - ASSERT(receiver.is(rax)); // Used for parameter count. - ASSERT(function.is(rdi)); // Required by InvokeFunction. - ASSERT(ToRegister(instr->result()).is(rax)); // If the receiver is null or undefined, we have to pass the global // object as a receiver to normal functions. Values have to be @@ -2594,6 +2589,17 @@ void LCodeGen::DoApplyArguments(LApplyArguments* instr) { __ movq(receiver, FieldOperand(receiver, JSGlobalObject::kGlobalReceiverOffset)); __ bind(&receiver_ok); +} + + +void LCodeGen::DoApplyArguments(LApplyArguments* instr) { + Register receiver = ToRegister(instr->receiver()); + Register function = ToRegister(instr->function()); + Register length = ToRegister(instr->length()); + Register elements = ToRegister(instr->elements()); + ASSERT(receiver.is(rax)); // Used for parameter count. + ASSERT(function.is(rdi)); // Required by InvokeFunction. + ASSERT(ToRegister(instr->result()).is(rax)); // Copy the arguments to this function possibly from the // adaptor frame below it. diff --git a/src/x64/lithium-x64.cc b/src/x64/lithium-x64.cc index e2569c9..d3e4cdd 100644 --- a/src/x64/lithium-x64.cc +++ b/src/x64/lithium-x64.cc @@ -1092,6 +1092,14 @@ LInstruction* LChunkBuilder::DoInstanceOfKnownGlobal( } +LInstruction* LChunkBuilder::DoWrapReceiver(HWrapReceiver* instr) { + LOperand* receiver = UseRegister(instr->receiver()); + LOperand* function = UseRegisterAtStart(instr->function()); + LWrapReceiver* result = new(zone()) LWrapReceiver(receiver, function); + return AssignEnvironment(DefineSameAsFirst(result)); +} + + LInstruction* LChunkBuilder::DoApplyArguments(HApplyArguments* instr) { LOperand* function = UseFixed(instr->function(), rdi); LOperand* receiver = UseFixed(instr->receiver(), rax); diff --git a/src/x64/lithium-x64.h b/src/x64/lithium-x64.h index 3d5d854..2d8fd2e 100644 --- a/src/x64/lithium-x64.h +++ b/src/x64/lithium-x64.h @@ -178,7 +178,8 @@ class LCodeGen; V(ForInCacheArray) \ V(CheckMapValue) \ V(LoadFieldByIndex) \ - V(DateField) + V(DateField) \ + V(WrapReceiver) #define DECLARE_CONCRETE_INSTRUCTION(type, mnemonic) \ @@ -469,6 +470,20 @@ class LControlInstruction: public LTemplateInstruction<0, I, T> { }; +class LWrapReceiver: public LTemplateInstruction<1, 2, 0> { + public: + LWrapReceiver(LOperand* receiver, LOperand* function) { + inputs_[0] = receiver; + inputs_[1] = function; + } + + DECLARE_CONCRETE_INSTRUCTION(WrapReceiver, "wrap-receiver") + + LOperand* receiver() { return inputs_[0]; } + LOperand* function() { return inputs_[1]; } +}; + + class LApplyArguments: public LTemplateInstruction<1, 4, 0> { public: LApplyArguments(LOperand* function, diff --git a/test/mjsunit/compiler/inline-arguments.js b/test/mjsunit/compiler/inline-arguments.js index 532fc26..b79f59c 100644 --- a/test/mjsunit/compiler/inline-arguments.js +++ b/test/mjsunit/compiler/inline-arguments.js @@ -27,11 +27,30 @@ // Flags: --allow-natives-syntax -// Test inlining functions that use arguments. -function f() { return g(1, 2, 3); } +function A() { +} -function g(x, y, z) { return %_ArgumentsLength(); } +A.prototype.X = function (a, b, c) { + assertTrue(this instanceof A); + assertEquals(1, a); + assertEquals(2, b); + assertEquals(3, c); +}; -for (var i = 0; i < 5; ++i) f(); -%OptimizeFunctionOnNextCall(f); -assertEquals(3, f()); +A.prototype.Y = function () { + this.X.apply(this, arguments); +}; + +A.prototype.Z = function () { + this.Y(1,2,3); +}; + +var a = new A(); +a.Z(4,5,6); +a.Z(4,5,6); +%OptimizeFunctionOnNextCall(a.Z); +a.Z(4,5,6); +A.prototype.X.apply = function (receiver, args) { + return Function.prototype.apply.call(this, receiver, args); +}; +a.Z(4,5,6);