Commit 164e3a41 authored by mmaly@chromium.org's avatar mmaly@chromium.org

Strict mode fixes.

- mutual inlining strict and non-strict functions in crankshaft.
- assignment to undefined variable with eval in scope.
- propagation of strict mode through lazy compilation.

BUG=
TEST=test/mjsunit/strict-mode.js test/mjsunit/strict-mode-opt.js

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@7561 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent ce5500e6
......@@ -1309,6 +1309,7 @@ class LStoreGlobalGeneric: public LTemplateInstruction<0, 2, 0> {
LOperand* global_object() { return InputAt(0); }
Handle<Object> name() const { return hydrogen()->name(); }
LOperand* value() { return InputAt(1); }
bool strict_mode() { return hydrogen()->strict_mode(); }
};
......@@ -1639,6 +1640,7 @@ class LStoreNamedGeneric: public LTemplateInstruction<0, 2, 0> {
LOperand* object() { return inputs_[0]; }
LOperand* value() { return inputs_[1]; }
Handle<Object> name() const { return hydrogen()->name(); }
bool strict_mode() { return hydrogen()->strict_mode(); }
};
......@@ -1678,6 +1680,7 @@ class LStoreKeyedGeneric: public LTemplateInstruction<0, 3, 0> {
LOperand* object() { return inputs_[0]; }
LOperand* key() { return inputs_[1]; }
LOperand* value() { return inputs_[2]; }
bool strict_mode() { return hydrogen()->strict_mode(); }
};
class LStoreKeyedSpecializedArrayElement: public LTemplateInstruction<0, 3, 0> {
......
......@@ -2242,7 +2242,9 @@ void LCodeGen::DoStoreGlobalGeneric(LStoreGlobalGeneric* instr) {
ASSERT(ToRegister(instr->value()).is(r0));
__ mov(r2, Operand(instr->name()));
Handle<Code> ic = isolate()->builtins()->StoreIC_Initialize();
Handle<Code> ic = instr->strict_mode()
? isolate()->builtins()->StoreIC_Initialize_Strict()
: isolate()->builtins()->StoreIC_Initialize();
CallCode(ic, RelocInfo::CODE_TARGET_CONTEXT, instr);
}
......@@ -3134,7 +3136,7 @@ void LCodeGen::DoStoreNamedGeneric(LStoreNamedGeneric* instr) {
// Name is always in r2.
__ mov(r2, Operand(instr->name()));
Handle<Code> ic = info_->is_strict_mode()
Handle<Code> ic = instr->strict_mode()
? isolate()->builtins()->StoreIC_Initialize_Strict()
: isolate()->builtins()->StoreIC_Initialize();
CallCode(ic, RelocInfo::CODE_TARGET, instr);
......@@ -3222,7 +3224,7 @@ void LCodeGen::DoStoreKeyedGeneric(LStoreKeyedGeneric* instr) {
ASSERT(ToRegister(instr->key()).is(r1));
ASSERT(ToRegister(instr->value()).is(r0));
Handle<Code> ic = info_->is_strict_mode()
Handle<Code> ic = instr->strict_mode()
? isolate()->builtins()->KeyedStoreIC_Initialize_Strict()
: isolate()->builtins()->KeyedStoreIC_Initialize();
CallCode(ic, RelocInfo::CODE_TARGET, instr);
......
......@@ -1728,7 +1728,6 @@ class FunctionLiteral: public Expression {
int start_position_;
int end_position_;
bool is_expression_;
bool strict_mode_;
int function_token_position_;
Handle<String> inferred_name_;
bool pretenure_;
......
......@@ -604,6 +604,12 @@ bool Compiler::CompileLazy(CompilationInfo* info) {
// parsing statistics.
HistogramTimerScope timer(isolate->counters()->compile_lazy());
// After parsing we know function's strict mode. Remember it.
if (info->function()->strict_mode()) {
shared->set_strict_mode(true);
info->MarkAsStrictMode();
}
// Compile the code.
if (!MakeCode(info)) {
if (!isolate->has_pending_exception()) {
......
......@@ -2910,8 +2910,10 @@ class HStoreGlobalGeneric: public HTemplateInstruction<3> {
HStoreGlobalGeneric(HValue* context,
HValue* global_object,
Handle<Object> name,
HValue* value)
: name_(name) {
HValue* value,
bool strict_mode)
: name_(name),
strict_mode_(strict_mode) {
SetOperandAt(0, context);
SetOperandAt(1, global_object);
SetOperandAt(2, value);
......@@ -2923,6 +2925,7 @@ class HStoreGlobalGeneric: public HTemplateInstruction<3> {
HValue* global_object() { return OperandAt(1); }
Handle<Object> name() const { return name_; }
HValue* value() { return OperandAt(2); }
bool strict_mode() { return strict_mode_; }
virtual void PrintDataTo(StringStream* stream);
......@@ -2934,6 +2937,7 @@ class HStoreGlobalGeneric: public HTemplateInstruction<3> {
private:
Handle<Object> name_;
bool strict_mode_;
};
......@@ -3265,8 +3269,10 @@ class HStoreNamedGeneric: public HTemplateInstruction<3> {
HStoreNamedGeneric(HValue* context,
HValue* object,
Handle<String> name,
HValue* value)
: name_(name) {
HValue* value,
bool strict_mode)
: name_(name),
strict_mode_(strict_mode) {
SetOperandAt(0, object);
SetOperandAt(1, value);
SetOperandAt(2, context);
......@@ -3277,6 +3283,7 @@ class HStoreNamedGeneric: public HTemplateInstruction<3> {
HValue* value() { return OperandAt(1); }
HValue* context() { return OperandAt(2); }
Handle<String> name() { return name_; }
bool strict_mode() { return strict_mode_; }
virtual void PrintDataTo(StringStream* stream);
......@@ -3288,6 +3295,7 @@ class HStoreNamedGeneric: public HTemplateInstruction<3> {
private:
Handle<String> name_;
bool strict_mode_;
};
......@@ -3365,7 +3373,9 @@ class HStoreKeyedGeneric: public HTemplateInstruction<4> {
HStoreKeyedGeneric(HValue* context,
HValue* object,
HValue* key,
HValue* value) {
HValue* value,
bool strict_mode)
: strict_mode_(strict_mode) {
SetOperandAt(0, object);
SetOperandAt(1, key);
SetOperandAt(2, value);
......@@ -3377,6 +3387,7 @@ class HStoreKeyedGeneric: public HTemplateInstruction<4> {
HValue* key() { return OperandAt(1); }
HValue* value() { return OperandAt(2); }
HValue* context() { return OperandAt(3); }
bool strict_mode() { return strict_mode_; }
virtual Representation RequiredInputRepresentation(int index) const {
return Representation::Tagged();
......@@ -3385,6 +3396,9 @@ class HStoreKeyedGeneric: public HTemplateInstruction<4> {
virtual void PrintDataTo(StringStream* stream);
DECLARE_CONCRETE_INSTRUCTION(StoreKeyedGeneric, "store_keyed_generic")
private:
bool strict_mode_;
};
......
......@@ -2981,7 +2981,12 @@ void HGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) {
HValue* value = Pop();
Handle<String> name = Handle<String>::cast(key->handle());
HStoreNamedGeneric* store =
new(zone()) HStoreNamedGeneric(context, literal, name, value);
new(zone()) HStoreNamedGeneric(
context,
literal,
name,
value,
function_strict_mode());
AddInstruction(store);
AddSimulate(key->id());
} else {
......@@ -3122,7 +3127,12 @@ HInstruction* HGraphBuilder::BuildStoreNamedGeneric(HValue* object,
HValue* value) {
HContext* context = new(zone()) HContext;
AddInstruction(context);
return new(zone()) HStoreNamedGeneric(context, object, name, value);
return new(zone()) HStoreNamedGeneric(
context,
object,
name,
value,
function_strict_mode());
}
......@@ -3300,7 +3310,8 @@ void HGraphBuilder::HandleGlobalVariableAssignment(Variable* var,
new(zone()) HStoreGlobalGeneric(context,
global_object,
var->name(),
value);
value,
function_strict_mode());
instr->set_position(position);
AddInstruction(instr);
ASSERT(instr->HasSideEffects());
......@@ -3628,7 +3639,12 @@ HInstruction* HGraphBuilder::BuildStoreKeyedGeneric(HValue* object,
HValue* value) {
HContext* context = new(zone()) HContext;
AddInstruction(context);
return new(zone()) HStoreKeyedGeneric(context, object, key, value);
return new(zone()) HStoreKeyedGeneric(
context,
object,
key,
value,
function_strict_mode());
}
......
......@@ -709,6 +709,9 @@ class HGraphBuilder: public AstVisitor {
void ClearInlinedTestContext() {
function_state()->ClearInlinedTestContext();
}
bool function_strict_mode() {
return function_state()->compilation_info()->is_strict_mode();
}
// Generators for inline runtime functions.
#define INLINE_FUNCTION_GENERATOR_DECLARATION(Name, argc, ressize) \
......
......@@ -2108,7 +2108,9 @@ void LCodeGen::DoStoreGlobalGeneric(LStoreGlobalGeneric* instr) {
ASSERT(ToRegister(instr->value()).is(eax));
__ mov(ecx, instr->name());
Handle<Code> ic = isolate()->builtins()->StoreIC_Initialize();
Handle<Code> ic = instr->strict_mode()
? isolate()->builtins()->StoreIC_Initialize_Strict()
: isolate()->builtins()->StoreIC_Initialize();
CallCode(ic, RelocInfo::CODE_TARGET_CONTEXT, instr, CONTEXT_ADJUSTED);
}
......@@ -2997,7 +2999,7 @@ void LCodeGen::DoStoreNamedGeneric(LStoreNamedGeneric* instr) {
ASSERT(ToRegister(instr->value()).is(eax));
__ mov(ecx, instr->name());
Handle<Code> ic = info_->is_strict_mode()
Handle<Code> ic = instr->strict_mode()
? isolate()->builtins()->StoreIC_Initialize_Strict()
: isolate()->builtins()->StoreIC_Initialize();
CallCode(ic, RelocInfo::CODE_TARGET, instr, CONTEXT_ADJUSTED);
......@@ -3097,7 +3099,7 @@ void LCodeGen::DoStoreKeyedGeneric(LStoreKeyedGeneric* instr) {
ASSERT(ToRegister(instr->key()).is(ecx));
ASSERT(ToRegister(instr->value()).is(eax));
Handle<Code> ic = info_->is_strict_mode()
Handle<Code> ic = instr->strict_mode()
? isolate()->builtins()->KeyedStoreIC_Initialize_Strict()
: isolate()->builtins()->KeyedStoreIC_Initialize();
CallCode(ic, RelocInfo::CODE_TARGET, instr, CONTEXT_ADJUSTED);
......
......@@ -1346,6 +1346,7 @@ class LStoreGlobalGeneric: public LTemplateInstruction<0, 3, 0> {
LOperand* global_object() { return InputAt(1); }
Handle<Object> name() const { return hydrogen()->name(); }
LOperand* value() { return InputAt(2); }
bool strict_mode() { return hydrogen()->strict_mode(); }
};
......@@ -1694,6 +1695,7 @@ class LStoreNamedGeneric: public LTemplateInstruction<0, 3, 0> {
LOperand* object() { return inputs_[1]; }
LOperand* value() { return inputs_[2]; }
Handle<Object> name() const { return hydrogen()->name(); }
bool strict_mode() { return hydrogen()->strict_mode(); }
};
......@@ -1755,6 +1757,7 @@ class LStoreKeyedGeneric: public LTemplateInstruction<0, 4, 0> {
}
DECLARE_CONCRETE_INSTRUCTION(StoreKeyedGeneric, "store-keyed-generic")
DECLARE_HYDROGEN_ACCESSOR(StoreKeyedGeneric)
virtual void PrintDataTo(StringStream* stream);
......@@ -1762,6 +1765,7 @@ class LStoreKeyedGeneric: public LTemplateInstruction<0, 4, 0> {
LOperand* object() { return inputs_[1]; }
LOperand* key() { return inputs_[2]; }
LOperand* value() { return inputs_[3]; }
bool strict_mode() { return hydrogen()->strict_mode(); }
};
......
......@@ -7820,8 +7820,17 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StoreContextSlot) {
// The property exists in the extension context.
context_ext = Handle<JSObject>::cast(holder);
} else {
// The property was not found. It needs to be stored in the global context.
// The property was not found.
ASSERT(attributes == ABSENT);
if (strict_mode == kStrictMode) {
// Throw in strict mode (assignment to undefined variable).
Handle<Object> error =
isolate->factory()->NewReferenceError(
"not_defined", HandleVector(&name, 1));
return isolate->Throw(*error);
}
// In non-strict mode, the property is stored in the global context.
attributes = NONE;
context_ext = Handle<JSObject>(isolate->context()->global());
}
......
......@@ -2115,7 +2115,9 @@ void LCodeGen::DoStoreGlobalGeneric(LStoreGlobalGeneric* instr) {
ASSERT(ToRegister(instr->value()).is(rax));
__ Move(rcx, instr->name());
Handle<Code> ic = isolate()->builtins()->StoreIC_Initialize();
Handle<Code> ic = instr->strict_mode()
? isolate()->builtins()->StoreIC_Initialize_Strict()
: isolate()->builtins()->StoreIC_Initialize();
CallCode(ic, RelocInfo::CODE_TARGET_CONTEXT, instr);
}
......@@ -2979,7 +2981,7 @@ void LCodeGen::DoStoreNamedGeneric(LStoreNamedGeneric* instr) {
ASSERT(ToRegister(instr->value()).is(rax));
__ Move(rcx, instr->hydrogen()->name());
Handle<Code> ic = info_->is_strict_mode()
Handle<Code> ic = instr->strict_mode()
? isolate()->builtins()->StoreIC_Initialize_Strict()
: isolate()->builtins()->StoreIC_Initialize();
CallCode(ic, RelocInfo::CODE_TARGET, instr);
......@@ -3075,7 +3077,7 @@ void LCodeGen::DoStoreKeyedGeneric(LStoreKeyedGeneric* instr) {
ASSERT(ToRegister(instr->key()).is(rcx));
ASSERT(ToRegister(instr->value()).is(rax));
Handle<Code> ic = info_->is_strict_mode()
Handle<Code> ic = instr->strict_mode()
? isolate()->builtins()->KeyedStoreIC_Initialize_Strict()
: isolate()->builtins()->KeyedStoreIC_Initialize();
CallCode(ic, RelocInfo::CODE_TARGET, instr);
......
......@@ -1295,6 +1295,7 @@ class LStoreGlobalGeneric: public LTemplateInstruction<0, 2, 0> {
LOperand* global_object() { return InputAt(0); }
Handle<Object> name() const { return hydrogen()->name(); }
LOperand* value() { return InputAt(1); }
bool strict_mode() { return hydrogen()->strict_mode(); }
};
......@@ -1616,6 +1617,7 @@ class LStoreNamedGeneric: public LTemplateInstruction<0, 2, 0> {
LOperand* object() { return inputs_[0]; }
LOperand* value() { return inputs_[1]; }
Handle<Object> name() const { return hydrogen()->name(); }
bool strict_mode() { return hydrogen()->strict_mode(); }
};
......@@ -1671,12 +1673,14 @@ class LStoreKeyedGeneric: public LTemplateInstruction<0, 3, 0> {
}
DECLARE_CONCRETE_INSTRUCTION(StoreKeyedGeneric, "store-keyed-generic")
DECLARE_HYDROGEN_ACCESSOR(StoreKeyedGeneric)
virtual void PrintDataTo(StringStream* stream);
LOperand* object() { return inputs_[0]; }
LOperand* key() { return inputs_[1]; }
LOperand* value() { return inputs_[2]; }
bool strict_mode() { return hydrogen()->strict_mode(); }
};
......
// Copyright 2011 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.
var global = 0;
var MAX = 1000000;
// Attempt to inline strcit in non-strict.
function strictToBeInlined(n) {
"use strict";
global = "strict";
if (n == MAX) { undefined_variable_strict = "value"; }
}
function nonstrictCallStrict(n) {
strictToBeInlined(n);
}
(function testInlineStrictInNonStrict() {
for (var i = 0; i <= MAX; i ++) {
try {
nonstrictCallStrict(i);
} catch (e) {
assertInstanceof(e, ReferenceError);
assertEquals(MAX, i);
return;
}
}
fail("ReferenceError after MAX iterations", "no exception");
})();
// Attempt to inline non-strict in strict.
function nonstrictToBeInlined(n) {
global = "nonstrict";
if (n == MAX) { undefined_variable_nonstrict = "The nonstrict value"; }
}
function strictCallNonStrict(n) {
"use strict";
nonstrictToBeInlined(n);
}
(function testInlineNonStrictInStrict() {
for (var i = 0; i <= MAX; i ++) {
try {
strictCallNonStrict(i);
} catch (e) {
fail("no exception", "exception");
}
}
assertEquals("The nonstrict value", undefined_variable_nonstrict);
})();
// Optimize strict function.
function strictAssignToUndefined(n) {
"use strict";
global = "strict";
if (n == MAX) { undefined_variable_strict_2 = "value"; }
}
(function testOptimizeStrictAssignToUndefined() {
for (var i = 0; i <= MAX; i ++) {
try {
strictAssignToUndefined(i);
} catch (e) {
assertInstanceof(e, ReferenceError);
assertEquals(MAX, i);
return;
}
}
fail("ReferenceError after MAX iterations", "no exception");
})();
......@@ -387,10 +387,9 @@ for (var i = 0; i < future_reserved_words.length; i++) {
testFutureReservedWord(future_reserved_words[i]);
}
function testAssignToUndefined(should_throw) {
"use strict";
function testAssignToUndefined(test, should_throw) {
try {
possibly_undefined_variable_for_strict_mode_test = "should throw?";
test();
} catch (e) {
assertTrue(should_throw, "strict mode");
assertInstanceof(e, ReferenceError, "strict mode");
......@@ -399,33 +398,78 @@ function testAssignToUndefined(should_throw) {
assertFalse(should_throw, "strict mode");
}
testAssignToUndefined(true);
testAssignToUndefined(true);
testAssignToUndefined(true);
function repeat(n, f) {
for (var i = 0; i < n; i ++) { f(); }
}
function assignToUndefined() {
"use strict";
possibly_undefined_variable_for_strict_mode_test = "should throw?";
}
testAssignToUndefined(assignToUndefined, true);
testAssignToUndefined(assignToUndefined, true);
testAssignToUndefined(assignToUndefined, true);
possibly_undefined_variable_for_strict_mode_test = "value";
testAssignToUndefined(false);
testAssignToUndefined(false);
testAssignToUndefined(false);
testAssignToUndefined(assignToUndefined, false);
testAssignToUndefined(assignToUndefined, false);
testAssignToUndefined(assignToUndefined, false);
delete possibly_undefined_variable_for_strict_mode_test;
testAssignToUndefined(true);
testAssignToUndefined(true);
testAssignToUndefined(true);
testAssignToUndefined(assignToUndefined, true);
testAssignToUndefined(assignToUndefined, true);
testAssignToUndefined(assignToUndefined, true);
function repeat(n, f) {
for (var i = 0; i < n; i ++) { f(); }
}
repeat(10, function() { testAssignToUndefined(true); });
repeat(10, function() { testAssignToUndefined(assignToUndefined, true); });
possibly_undefined_variable_for_strict_mode_test = "value";
repeat(10, function() { testAssignToUndefined(false); });
repeat(10, function() { testAssignToUndefined(assignToUndefined, false); });
delete possibly_undefined_variable_for_strict_mode_test;
repeat(10, function() { testAssignToUndefined(true); });
repeat(10, function() { testAssignToUndefined(assignToUndefined, true); });
possibly_undefined_variable_for_strict_mode_test = undefined;
repeat(10, function() { testAssignToUndefined(false); });
repeat(10, function() { testAssignToUndefined(assignToUndefined, false); });
function assignToUndefinedWithEval() {
"use strict";
possibly_undefined_variable_for_strict_mode_test_with_eval = "should throw?";
eval("");
}
testAssignToUndefined(assignToUndefinedWithEval, true);
testAssignToUndefined(assignToUndefinedWithEval, true);
testAssignToUndefined(assignToUndefinedWithEval, true);
possibly_undefined_variable_for_strict_mode_test_with_eval = "value";
testAssignToUndefined(assignToUndefinedWithEval, false);
testAssignToUndefined(assignToUndefinedWithEval, false);
testAssignToUndefined(assignToUndefinedWithEval, false);
delete possibly_undefined_variable_for_strict_mode_test_with_eval;
testAssignToUndefined(assignToUndefinedWithEval, true);
testAssignToUndefined(assignToUndefinedWithEval, true);
testAssignToUndefined(assignToUndefinedWithEval, true);
repeat(10, function() {
testAssignToUndefined(assignToUndefinedWithEval, true);
});
possibly_undefined_variable_for_strict_mode_test_with_eval = "value";
repeat(10, function() {
testAssignToUndefined(assignToUndefinedWithEval, false);
});
delete possibly_undefined_variable_for_strict_mode_test_with_eval;
repeat(10, function() {
testAssignToUndefined(assignToUndefinedWithEval, true);
});
possibly_undefined_variable_for_strict_mode_test_with_eval = undefined;
repeat(10, function() {
testAssignToUndefined(assignToUndefinedWithEval, false);
});
(function testDeleteNonConfigurable() {
function delete_property(o) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment