| OLD | NEW |
| 1 // Copyright 2006-2008 the V8 project authors. All rights reserved. | 1 // Copyright 2006-2008 the V8 project authors. All rights reserved. |
| 2 // Redistribution and use in source and binary forms, with or without | 2 // Redistribution and use in source and binary forms, with or without |
| 3 // modification, are permitted provided that the following conditions are | 3 // modification, are permitted provided that the following conditions are |
| 4 // met: | 4 // met: |
| 5 // | 5 // |
| 6 // * Redistributions of source code must retain the above copyright | 6 // * Redistributions of source code must retain the above copyright |
| 7 // notice, this list of conditions and the following disclaimer. | 7 // notice, this list of conditions and the following disclaimer. |
| 8 // * Redistributions in binary form must reproduce the above | 8 // * Redistributions in binary form must reproduce the above |
| 9 // copyright notice, this list of conditions and the following | 9 // copyright notice, this list of conditions and the following |
| 10 // disclaimer in the documentation and/or other materials provided | 10 // disclaimer in the documentation and/or other materials provided |
| (...skipping 326 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 337 return frame_->ParameterAt(index); | 337 return frame_->ParameterAt(index); |
| 338 | 338 |
| 339 case Slot::LOCAL: | 339 case Slot::LOCAL: |
| 340 return frame_->LocalAt(index); | 340 return frame_->LocalAt(index); |
| 341 | 341 |
| 342 case Slot::CONTEXT: { | 342 case Slot::CONTEXT: { |
| 343 // Follow the context chain if necessary. | 343 // Follow the context chain if necessary. |
| 344 ASSERT(!tmp.is(esi)); // do not overwrite context register | 344 ASSERT(!tmp.is(esi)); // do not overwrite context register |
| 345 Register context = esi; | 345 Register context = esi; |
| 346 int chain_length = scope()->ContextChainLength(slot->var()->scope()); | 346 int chain_length = scope()->ContextChainLength(slot->var()->scope()); |
| 347 for (int i = chain_length; i-- > 0;) { | 347 for (int i = 0; i < chain_length; i++) { |
| 348 // Load the closure. | 348 // Load the closure. |
| 349 // (All contexts, even 'with' contexts, have a closure, | 349 // (All contexts, even 'with' contexts, have a closure, |
| 350 // and it is the same for all contexts inside a function. | 350 // and it is the same for all contexts inside a function. |
| 351 // There is no need to go to the function context first.) | 351 // There is no need to go to the function context first.) |
| 352 __ mov(tmp, ContextOperand(context, Context::CLOSURE_INDEX)); | 352 __ mov(tmp, ContextOperand(context, Context::CLOSURE_INDEX)); |
| 353 // Load the function context (which is the incoming, outer context). | 353 // Load the function context (which is the incoming, outer context). |
| 354 __ mov(tmp, FieldOperand(tmp, JSFunction::kContextOffset)); | 354 __ mov(tmp, FieldOperand(tmp, JSFunction::kContextOffset)); |
| 355 context = tmp; | 355 context = tmp; |
| 356 } | 356 } |
| 357 // We may have a 'with' context now. Get the function context. | 357 // We may have a 'with' context now. Get the function context. |
| 358 // (In fact this mov may never be the needed, since the scope analysis | 358 // (In fact this mov may never be the needed, since the scope analysis |
| 359 // may not permit a direct context access in this case and thus we are | 359 // may not permit a direct context access in this case and thus we are |
| 360 // always at a function context. However it is safe to dereference be- | 360 // always at a function context. However it is safe to dereference be- |
| 361 // cause the function context of a function context is itself. Before | 361 // cause the function context of a function context is itself. Before |
| 362 // deleting this mov we should try to create a counter-example first, | 362 // deleting this mov we should try to create a counter-example first, |
| 363 // though...) | 363 // though...) |
| 364 __ mov(tmp, ContextOperand(context, Context::FCONTEXT_INDEX)); | 364 __ mov(tmp, ContextOperand(context, Context::FCONTEXT_INDEX)); |
| 365 return ContextOperand(tmp, index); | 365 return ContextOperand(tmp, index); |
| 366 } | 366 } |
| 367 | 367 |
| 368 default: | 368 default: |
| 369 UNREACHABLE(); | 369 UNREACHABLE(); |
| 370 return Operand(eax); | 370 return Operand(eax); |
| 371 } | 371 } |
| 372 } | 372 } |
| 373 | 373 |
| 374 | 374 |
| 375 Operand CodeGenerator::ContextSlotOperandCheckExtensions(Slot* slot, |
| 376 Result tmp, |
| 377 JumpTarget* slow) { |
| 378 ASSERT(slot->type() == Slot::CONTEXT); |
| 379 ASSERT(tmp.is_register()); |
| 380 Result context(esi, this); |
| 381 |
| 382 for (Scope* s = scope(); s != slot->var()->scope(); s = s->outer_scope()) { |
| 383 if (s->num_heap_slots() > 0) { |
| 384 if (s->calls_eval()) { |
| 385 // Check that extension is NULL. |
| 386 __ cmp(ContextOperand(context.reg(), Context::EXTENSION_INDEX), |
| 387 Immediate(0)); |
| 388 slow->Branch(not_equal, not_taken); |
| 389 } |
| 390 __ mov(tmp.reg(), ContextOperand(context.reg(), Context::CLOSURE_INDEX)); |
| 391 __ mov(tmp.reg(), FieldOperand(tmp.reg(), JSFunction::kContextOffset)); |
| 392 context = tmp; |
| 393 } |
| 394 } |
| 395 // Check that last extension is NULL. |
| 396 __ cmp(ContextOperand(context.reg(), Context::EXTENSION_INDEX), |
| 397 Immediate(0)); |
| 398 slow->Branch(not_equal, not_taken); |
| 399 __ mov(tmp.reg(), ContextOperand(context.reg(), Context::FCONTEXT_INDEX)); |
| 400 return ContextOperand(tmp.reg(), slot->index()); |
| 401 } |
| 402 |
| 403 |
| 375 // Emit code to load the value of an expression to the top of the | 404 // Emit code to load the value of an expression to the top of the |
| 376 // frame. If the expression is boolean-valued it may be compiled (or | 405 // frame. If the expression is boolean-valued it may be compiled (or |
| 377 // partially compiled) into control flow to the control destination. | 406 // partially compiled) into control flow to the control destination. |
| 378 // If force_control is true, control flow is forced. | 407 // If force_control is true, control flow is forced. |
| 379 void CodeGenerator::LoadCondition(Expression* x, | 408 void CodeGenerator::LoadCondition(Expression* x, |
| 380 TypeofState typeof_state, | 409 TypeofState typeof_state, |
| 381 ControlDestination* dest, | 410 ControlDestination* dest, |
| 382 bool force_control) { | 411 bool force_control) { |
| 383 ASSERT(!in_spilled_code()); | 412 ASSERT(!in_spilled_code()); |
| 384 #ifdef DEBUG | 413 #ifdef DEBUG |
| (...skipping 1164 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1549 Variable* var = node->proxy()->var(); | 1578 Variable* var = node->proxy()->var(); |
| 1550 ASSERT(var != NULL); // must have been resolved | 1579 ASSERT(var != NULL); // must have been resolved |
| 1551 Slot* slot = var->slot(); | 1580 Slot* slot = var->slot(); |
| 1552 | 1581 |
| 1553 // If it was not possible to allocate the variable at compile time, | 1582 // If it was not possible to allocate the variable at compile time, |
| 1554 // we need to "declare" it at runtime to make sure it actually | 1583 // we need to "declare" it at runtime to make sure it actually |
| 1555 // exists in the local context. | 1584 // exists in the local context. |
| 1556 if (slot != NULL && slot->type() == Slot::LOOKUP) { | 1585 if (slot != NULL && slot->type() == Slot::LOOKUP) { |
| 1557 // Variables with a "LOOKUP" slot were introduced as non-locals | 1586 // Variables with a "LOOKUP" slot were introduced as non-locals |
| 1558 // during variable resolution and must have mode DYNAMIC. | 1587 // during variable resolution and must have mode DYNAMIC. |
| 1559 ASSERT(var->mode() == Variable::DYNAMIC); | 1588 ASSERT(var->is_dynamic()); |
| 1560 // For now, just do a runtime call. Duplicate the context register. | 1589 // For now, just do a runtime call. Duplicate the context register. |
| 1561 Result context(esi, this); | 1590 Result context(esi, this); |
| 1562 frame_->Push(&context); | 1591 frame_->Push(&context); |
| 1563 frame_->Push(var->name()); | 1592 frame_->Push(var->name()); |
| 1564 // Declaration nodes are always introduced in one of two modes. | 1593 // Declaration nodes are always introduced in one of two modes. |
| 1565 ASSERT(node->mode() == Variable::VAR || node->mode() == Variable::CONST); | 1594 ASSERT(node->mode() == Variable::VAR || node->mode() == Variable::CONST); |
| 1566 PropertyAttributes attr = node->mode() == Variable::VAR ? NONE : READ_ONLY; | 1595 PropertyAttributes attr = node->mode() == Variable::VAR ? NONE : READ_ONLY; |
| 1567 frame_->Push(Smi::FromInt(attr)); | 1596 frame_->Push(Smi::FromInt(attr)); |
| 1568 // Push initial value, if any. | 1597 // Push initial value, if any. |
| 1569 // Note: For variables we must not push an initial value (such as | 1598 // Note: For variables we must not push an initial value (such as |
| (...skipping 1322 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2892 Load(node->else_expression(), typeof_state()); | 2921 Load(node->else_expression(), typeof_state()); |
| 2893 } | 2922 } |
| 2894 } | 2923 } |
| 2895 | 2924 |
| 2896 exit.Bind(); | 2925 exit.Bind(); |
| 2897 } | 2926 } |
| 2898 | 2927 |
| 2899 | 2928 |
| 2900 void CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) { | 2929 void CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) { |
| 2901 if (slot->type() == Slot::LOOKUP) { | 2930 if (slot->type() == Slot::LOOKUP) { |
| 2902 ASSERT(slot->var()->mode() == Variable::DYNAMIC); | 2931 ASSERT(slot->var()->is_dynamic()); |
| 2903 | 2932 |
| 2904 // For now, just do a runtime call. | 2933 JumpTarget slow(this); |
| 2934 JumpTarget done(this); |
| 2935 Result value(this); |
| 2936 |
| 2937 // Generate fast-case code for variables that might be shadowed by |
| 2938 // eval-introduced variables. Eval is used a lot without |
| 2939 // introducing variables. In those cases, we do not want to |
| 2940 // perform a runtime call for all variables in the scope |
| 2941 // containing the eval. |
| 2942 if (slot->var()->mode() == Variable::DYNAMIC_GLOBAL) { |
| 2943 value = LoadFromGlobalSlotCheckExtensions(slot, typeof_state, &slow); |
| 2944 // If there was no control flow to slow, we can exit early. |
| 2945 if (!slow.is_linked()) { |
| 2946 frame_->Push(&value); |
| 2947 return; |
| 2948 } |
| 2949 |
| 2950 done.Jump(&value); |
| 2951 |
| 2952 } else if (slot->var()->mode() == Variable::DYNAMIC_LOCAL) { |
| 2953 Slot* potential_slot = slot->var()->local_if_not_shadowed()->slot(); |
| 2954 // Allocate a fresh register to use as a temp in |
| 2955 // ContextSlotOperandCheckExtensions and to hold the result |
| 2956 // value. |
| 2957 value = allocator_->Allocate(); |
| 2958 ASSERT(value.is_valid()); |
| 2959 __ mov(value.reg(), |
| 2960 ContextSlotOperandCheckExtensions(potential_slot, value, &slow)); |
| 2961 // There is always control flow to slow from |
| 2962 // ContextSlotOperandCheckExtensions. |
| 2963 done.Jump(&value); |
| 2964 } |
| 2965 |
| 2966 slow.Bind(); |
| 2905 frame_->Push(esi); | 2967 frame_->Push(esi); |
| 2906 frame_->Push(slot->var()->name()); | 2968 frame_->Push(slot->var()->name()); |
| 2907 | |
| 2908 Result value(this); | |
| 2909 if (typeof_state == INSIDE_TYPEOF) { | 2969 if (typeof_state == INSIDE_TYPEOF) { |
| 2910 value = | 2970 value = |
| 2911 frame_->CallRuntime(Runtime::kLoadContextSlotNoReferenceError, 2); | 2971 frame_->CallRuntime(Runtime::kLoadContextSlotNoReferenceError, 2); |
| 2912 } else { | 2972 } else { |
| 2913 value = frame_->CallRuntime(Runtime::kLoadContextSlot, 2); | 2973 value = frame_->CallRuntime(Runtime::kLoadContextSlot, 2); |
| 2914 } | 2974 } |
| 2975 |
| 2976 done.Bind(&value); |
| 2915 frame_->Push(&value); | 2977 frame_->Push(&value); |
| 2916 | 2978 |
| 2917 } else if (slot->var()->mode() == Variable::CONST) { | 2979 } else if (slot->var()->mode() == Variable::CONST) { |
| 2918 // Const slots may contain 'the hole' value (the constant hasn't been | 2980 // Const slots may contain 'the hole' value (the constant hasn't been |
| 2919 // initialized yet) which needs to be converted into the 'undefined' | 2981 // initialized yet) which needs to be converted into the 'undefined' |
| 2920 // value. | 2982 // value. |
| 2921 // | 2983 // |
| 2922 // We currently spill the virtual frame because constants use the | 2984 // We currently spill the virtual frame because constants use the |
| 2923 // potentially unsafe direct-frame access of SlotOperand. | 2985 // potentially unsafe direct-frame access of SlotOperand. |
| 2924 VirtualFrame::SpilledScope spilled_scope(this); | 2986 VirtualFrame::SpilledScope spilled_scope(this); |
| (...skipping 20 matching lines...) Expand all Loading... |
| 2945 // because it will always be a context slot. | 3007 // because it will always be a context slot. |
| 2946 ASSERT(slot->type() == Slot::CONTEXT); | 3008 ASSERT(slot->type() == Slot::CONTEXT); |
| 2947 Result temp = allocator_->Allocate(); | 3009 Result temp = allocator_->Allocate(); |
| 2948 ASSERT(temp.is_valid()); | 3010 ASSERT(temp.is_valid()); |
| 2949 __ mov(temp.reg(), SlotOperand(slot, temp.reg())); | 3011 __ mov(temp.reg(), SlotOperand(slot, temp.reg())); |
| 2950 frame_->Push(&temp); | 3012 frame_->Push(&temp); |
| 2951 } | 3013 } |
| 2952 } | 3014 } |
| 2953 | 3015 |
| 2954 | 3016 |
| 3017 Result CodeGenerator::LoadFromGlobalSlotCheckExtensions( |
| 3018 Slot* slot, |
| 3019 TypeofState typeof_state, |
| 3020 JumpTarget* slow) { |
| 3021 // Check that no extension objects have been created by calls to |
| 3022 // eval from the current scope to the global scope. |
| 3023 Result context(esi, this); |
| 3024 Result tmp = allocator_->Allocate(); |
| 3025 ASSERT(tmp.is_valid()); // Called with all non-reserved registers available. |
| 3026 |
| 3027 for (Scope* s = scope(); s != NULL; s = s->outer_scope()) { |
| 3028 if (s->num_heap_slots() > 0) { |
| 3029 if (s->calls_eval()) { |
| 3030 // Check that extension is NULL. |
| 3031 __ cmp(ContextOperand(context.reg(), Context::EXTENSION_INDEX), |
| 3032 Immediate(0)); |
| 3033 slow->Branch(not_equal, not_taken); |
| 3034 } |
| 3035 // Load next context in chain. |
| 3036 __ mov(tmp.reg(), ContextOperand(context.reg(), Context::CLOSURE_INDEX)); |
| 3037 __ mov(tmp.reg(), FieldOperand(tmp.reg(), JSFunction::kContextOffset)); |
| 3038 context = tmp; |
| 3039 } |
| 3040 // If no outer scope calls eval, we do not need to check more |
| 3041 // context extensions. |
| 3042 if (!s->outer_scope_calls_eval()) break; |
| 3043 } |
| 3044 context.Unuse(); |
| 3045 tmp.Unuse(); |
| 3046 |
| 3047 // All extension objects were empty and it is safe to use a global |
| 3048 // load IC call. |
| 3049 Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize)); |
| 3050 // Load the global object. |
| 3051 LoadGlobal(); |
| 3052 // Setup the name register. All non-reserved registers are available. |
| 3053 Result name = allocator_->Allocate(ecx); |
| 3054 ASSERT(name.is_valid()); |
| 3055 __ mov(name.reg(), slot->var()->name()); |
| 3056 RelocInfo::Mode rmode = (typeof_state == INSIDE_TYPEOF) |
| 3057 ? RelocInfo::CODE_TARGET |
| 3058 : RelocInfo::CODE_TARGET_CONTEXT; |
| 3059 Result answer = frame_->CallCodeObject(ic, rmode, &name, 0); |
| 3060 |
| 3061 // Discard the global object. The result is in answer. |
| 3062 frame_->Drop(); |
| 3063 return answer; |
| 3064 } |
| 3065 |
| 3066 |
| 2955 void CodeGenerator::StoreToSlot(Slot* slot, InitState init_state) { | 3067 void CodeGenerator::StoreToSlot(Slot* slot, InitState init_state) { |
| 2956 if (slot->type() == Slot::LOOKUP) { | 3068 if (slot->type() == Slot::LOOKUP) { |
| 2957 ASSERT(slot->var()->mode() == Variable::DYNAMIC); | 3069 ASSERT(slot->var()->is_dynamic()); |
| 2958 | 3070 |
| 2959 // For now, just do a runtime call. | 3071 // For now, just do a runtime call. |
| 2960 frame_->Push(esi); | 3072 frame_->Push(esi); |
| 2961 frame_->Push(slot->var()->name()); | 3073 frame_->Push(slot->var()->name()); |
| 2962 | 3074 |
| 2963 Result value(this); | 3075 Result value(this); |
| 2964 if (init_state == CONST_INIT) { | 3076 if (init_state == CONST_INIT) { |
| 2965 // Same as the case for a normal store, but ignores attribute | 3077 // Same as the case for a normal store, but ignores attribute |
| 2966 // (e.g. READ_ONLY) of context slot so that we can initialize const | 3078 // (e.g. READ_ONLY) of context slot so that we can initialize const |
| 2967 // properties (introduced via eval("const foo = (some expr);")). Also, | 3079 // properties (introduced via eval("const foo = (some expr);")). Also, |
| (...skipping 11 matching lines...) Expand all Loading... |
| 2979 value = frame_->CallRuntime(Runtime::kInitializeConstContextSlot, 3); | 3091 value = frame_->CallRuntime(Runtime::kInitializeConstContextSlot, 3); |
| 2980 } else { | 3092 } else { |
| 2981 value = frame_->CallRuntime(Runtime::kStoreContextSlot, 3); | 3093 value = frame_->CallRuntime(Runtime::kStoreContextSlot, 3); |
| 2982 } | 3094 } |
| 2983 // Storing a variable must keep the (new) value on the expression | 3095 // Storing a variable must keep the (new) value on the expression |
| 2984 // stack. This is necessary for compiling chained assignment | 3096 // stack. This is necessary for compiling chained assignment |
| 2985 // expressions. | 3097 // expressions. |
| 2986 frame_->Push(&value); | 3098 frame_->Push(&value); |
| 2987 | 3099 |
| 2988 } else { | 3100 } else { |
| 2989 ASSERT(slot->var()->mode() != Variable::DYNAMIC); | 3101 ASSERT(!slot->var()->is_dynamic()); |
| 2990 | 3102 |
| 2991 JumpTarget exit(this); | 3103 JumpTarget exit(this); |
| 2992 if (init_state == CONST_INIT) { | 3104 if (init_state == CONST_INIT) { |
| 2993 ASSERT(slot->var()->mode() == Variable::CONST); | 3105 ASSERT(slot->var()->mode() == Variable::CONST); |
| 2994 // Only the first const initialization must be executed (the slot | 3106 // Only the first const initialization must be executed (the slot |
| 2995 // still contains 'the hole' value). When the assignment is executed, | 3107 // still contains 'the hole' value). When the assignment is executed, |
| 2996 // the code is identical to a normal store (see below). | 3108 // the code is identical to a normal store (see below). |
| 2997 // | 3109 // |
| 2998 // We spill the frame in the code below because the direct-frame | 3110 // We spill the frame in the code below because the direct-frame |
| 2999 // access of SlotOperand is potentially unsafe with an unspilled | 3111 // access of SlotOperand is potentially unsafe with an unspilled |
| (...skipping 3543 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 6543 | 6655 |
| 6544 // Slow-case: Go through the JavaScript implementation. | 6656 // Slow-case: Go through the JavaScript implementation. |
| 6545 __ bind(&slow); | 6657 __ bind(&slow); |
| 6546 __ InvokeBuiltin(Builtins::INSTANCE_OF, JUMP_FUNCTION); | 6658 __ InvokeBuiltin(Builtins::INSTANCE_OF, JUMP_FUNCTION); |
| 6547 } | 6659 } |
| 6548 | 6660 |
| 6549 | 6661 |
| 6550 #undef __ | 6662 #undef __ |
| 6551 | 6663 |
| 6552 } } // namespace v8::internal | 6664 } } // namespace v8::internal |
| OLD | NEW |