// Record source position for debugger.
SetSourcePosition(expr->position());
// Call the IC initialization code.
+ InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
Handle<Code> ic =
- isolate()->stub_cache()->ComputeCallInitialize(arg_count, mode);
+ isolate()->stub_cache()->ComputeCallInitialize(arg_count, in_loop, mode);
__ Call(ic, mode, expr->id());
RecordJSReturnSite(expr);
// Restore context register.
// Record source position for debugger.
SetSourcePosition(expr->position());
// Call the IC initialization code.
+ InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
Handle<Code> ic =
- isolate()->stub_cache()->ComputeKeyedCallInitialize(arg_count);
+ isolate()->stub_cache()->ComputeKeyedCallInitialize(arg_count, in_loop);
__ lw(a2, MemOperand(sp, (arg_count + 1) * kPointerSize)); // Key.
__ Call(ic, RelocInfo::CODE_TARGET, expr->id());
RecordJSReturnSite(expr);
}
// Record source position for debugger.
SetSourcePosition(expr->position());
- CallFunctionStub stub(arg_count, flags);
+ InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
+ CallFunctionStub stub(arg_count, in_loop, flags);
__ CallStub(&stub);
RecordJSReturnSite(expr);
// Restore context register.
}
// Record source position for debugger.
SetSourcePosition(expr->position());
- CallFunctionStub stub(arg_count, RECEIVER_MIGHT_BE_IMPLICIT);
+ InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
+ CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_IMPLICIT);
__ CallStub(&stub);
RecordJSReturnSite(expr);
// Restore context register.
__ li(a2, Operand(expr->name()));
RelocInfo::Mode mode = RelocInfo::CODE_TARGET;
Handle<Code> ic =
- isolate()->stub_cache()->ComputeCallInitialize(arg_count, mode);
+ isolate()->stub_cache()->ComputeCallInitialize(arg_count,
+ NOT_IN_LOOP,
+ mode);
__ Call(ic, mode, expr->id());
// Restore context register.
__ lw(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
// Probe the stub cache.
Code::Flags flags = Code::ComputeFlags(kind,
+ NOT_IN_LOOP,
MONOMORPHIC,
extra_ic_state,
NORMAL,
// -----------------------------------
// Probe the stub cache.
- Code::Flags flags = Code::ComputeFlags(Code::LOAD_IC, MONOMORPHIC);
+ Code::Flags flags = Code::ComputeFlags(Code::LOAD_IC,
+ NOT_IN_LOOP,
+ MONOMORPHIC);
Isolate::Current()->stub_cache()->GenerateProbe(
masm, flags, a0, a2, a3, t0, t1);
// -----------------------------------
// Get the receiver from the stack and probe the stub cache.
- Code::Flags flags =
- Code::ComputeFlags(Code::STORE_IC, MONOMORPHIC, strict_mode);
+ Code::Flags flags = Code::ComputeFlags(Code::STORE_IC,
+ NOT_IN_LOOP,
+ MONOMORPHIC,
+ strict_mode);
Isolate::Current()->stub_cache()->GenerateProbe(
masm, flags, a1, a2, a3, t0, t1);
LookupResult lookup;
global->Lookup(*name, &lookup);
if (lookup.IsProperty()) {
- // We found an existing property. Unless it was an interceptor
- // that claims the property is absent, skip this declaration.
- if (lookup.type() != INTERCEPTOR) {
- continue;
- }
+ // Determine if the property is local by comparing the holder
+ // against the global object. The information will be used to
+ // avoid throwing re-declaration errors when declaring
+ // variables or constants that exist in the prototype chain.
+ bool is_local = (*global == lookup.holder());
+ // Get the property attributes and determine if the property is
+ // read-only.
PropertyAttributes attributes = global->GetPropertyAttribute(*name);
- if (attributes != ABSENT) {
+ bool is_read_only = (attributes & READ_ONLY) != 0;
+ if (lookup.type() == INTERCEPTOR) {
+ // If the interceptor says the property is there, we
+ // just return undefined without overwriting the property.
+ // Otherwise, we continue to setting the property.
+ if (attributes != ABSENT) {
+ // Check if the existing property conflicts with regards to const.
+ if (is_local && (is_read_only || is_const_property)) {
+ const char* type = (is_read_only) ? "const" : "var";
+ return ThrowRedeclarationError(isolate, type, name);
+ };
+ // The property already exists without conflicting: Go to
+ // the next declaration.
+ continue;
+ }
+ // Fall-through and introduce the absent property by using
+ // SetProperty.
+ } else {
+ // For const properties, we treat a callback with this name
+ // even in the prototype as a conflicting declaration.
+ if (is_const_property && (lookup.type() == CALLBACKS)) {
+ return ThrowRedeclarationError(isolate, "const", name);
+ }
+ // Otherwise, we check for locally conflicting declarations.
+ if (is_local && (is_read_only || is_const_property)) {
+ const char* type = (is_read_only) ? "const" : "var";
+ return ThrowRedeclarationError(isolate, type, name);
+ }
+ // The property already exists without conflicting: Go to
+ // the next declaration.
continue;
}
- // Fall-through and introduce the absent property by using
- // SetProperty.
}
} else {
is_function_declaration = true;
LookupResult lookup;
global->LocalLookup(*name, &lookup);
+ // There's a local property that we need to overwrite because
+ // we're either declaring a function or there's an interceptor
+ // that claims the property is absent.
+ //
+ // Check for conflicting re-declarations. We cannot have
+ // conflicting types in case of intercepted properties because
+ // they are absent.
+ if (lookup.IsProperty() &&
+ (lookup.type() != INTERCEPTOR) &&
+ (lookup.IsReadOnly() || is_const_property)) {
+ const char* type = (lookup.IsReadOnly()) ? "const" : "var";
+ return ThrowRedeclarationError(isolate, type, name);
+ }
+
// Compute the property attributes. According to ECMA-262, section
// 13, page 71, the property must be read-only and
// non-deletable. However, neither SpiderMonkey nor KJS creates the
// to assign to the property.
// Note that objects can have hidden prototypes, so we need to traverse
// the whole chain of hidden prototypes to do a 'local' lookup.
- Object* object = global;
+ JSObject* real_holder = global;
LookupResult lookup;
- while (object->IsJSObject() &&
- JSObject::cast(object)->map()->is_hidden_prototype()) {
- JSObject* raw_holder = JSObject::cast(object);
- raw_holder->LocalLookup(*name, &lookup);
- if (lookup.IsProperty() && lookup.type() == INTERCEPTOR) {
- HandleScope handle_scope(isolate);
- Handle<JSObject> holder(raw_holder);
- PropertyAttributes intercepted = holder->GetPropertyAttribute(*name);
- // Update the raw pointer in case it's changed due to GC.
- raw_holder = *holder;
- if (intercepted != ABSENT && (intercepted & READ_ONLY) == 0) {
- // Found an interceptor that's not read only.
- if (assign) {
- return raw_holder->SetProperty(
- &lookup, *name, args[2], attributes, strict_mode);
- } else {
- return isolate->heap()->undefined_value();
+ while (true) {
+ real_holder->LocalLookup(*name, &lookup);
+ if (lookup.IsProperty()) {
+ // Determine if this is a redeclaration of something read-only.
+ if (lookup.IsReadOnly()) {
+ // If we found readonly property on one of hidden prototypes,
+ // just shadow it.
+ if (real_holder != isolate->context()->global()) break;
+ return ThrowRedeclarationError(isolate, "const", name);
+ }
+
+ // Determine if this is a redeclaration of an intercepted read-only
+ // property and figure out if the property exists at all.
+ bool found = true;
+ PropertyType type = lookup.type();
+ if (type == INTERCEPTOR) {
+ HandleScope handle_scope(isolate);
+ Handle<JSObject> holder(real_holder);
+ PropertyAttributes intercepted = holder->GetPropertyAttribute(*name);
+ real_holder = *holder;
+ if (intercepted == ABSENT) {
+ // The interceptor claims the property isn't there. We need to
+ // make sure to introduce it.
+ found = false;
+ } else if ((intercepted & READ_ONLY) != 0) {
+ // The property is present, but read-only. Since we're trying to
+ // overwrite it with a variable declaration we must throw a
+ // re-declaration error. However if we found readonly property
+ // on one of hidden prototypes, just shadow it.
+ if (real_holder != isolate->context()->global()) break;
+ return ThrowRedeclarationError(isolate, "const", name);
}
}
+
+ if (found && !assign) {
+ // The global property is there and we're not assigning any value
+ // to it. Just return.
+ return isolate->heap()->undefined_value();
+ }
+
+ // Assign the value (or undefined) to the property.
+ Object* value = (assign) ? args[2] : isolate->heap()->undefined_value();
+ return real_holder->SetProperty(
+ &lookup, *name, value, attributes, strict_mode);
}
- object = raw_holder->GetPrototype();
+
+ Object* proto = real_holder->GetPrototype();
+ if (!proto->IsJSObject())
+ break;
+
+ if (!JSObject::cast(proto)->map()->is_hidden_prototype())
+ break;
+
+ real_holder = JSObject::cast(proto);
}
- // Reload global in case the loop above performed a GC.
global = isolate->context()->global();
if (assign) {
return global->SetProperty(*name, args[2], attributes, strict_mode);
attributes);
}
- // Determine if this is a re-initialization of something not
+ // Determine if this is a redeclaration of something not
// read-only. In case the result is hidden behind an interceptor we
// need to ask it for the property attributes.
- if (!lookup.IsReadOnly() && lookup.type() == INTERCEPTOR) {
+ if (!lookup.IsReadOnly()) {
+ if (lookup.type() != INTERCEPTOR) {
+ return ThrowRedeclarationError(isolate, "var", name);
+ }
+
PropertyAttributes intercepted = global->GetPropertyAttribute(*name);
- // Ignore the re-initialization if the intercepted property is present
+ // Throw re-declaration error if the intercepted property is present
// but not read-only.
if (intercepted != ABSENT && (intercepted & READ_ONLY) == 0) {
- return isolate->heap()->undefined_value();
+ return ThrowRedeclarationError(isolate, "var", name);
}
// Restore global object from context (in case of GC) and continue
if (type == FIELD) {
FixedArray* properties = global->properties();
int index = lookup.GetFieldIndex();
- if (properties->get(index)->IsTheHole() || !lookup.IsReadOnly()) {
+ if (properties->get(index)->IsTheHole()) {
properties->set(index, *value);
}
} else if (type == NORMAL) {
- if (global->GetNormalizedProperty(&lookup)->IsTheHole() ||
- !lookup.IsReadOnly()) {
+ if (global->GetNormalizedProperty(&lookup)->IsTheHole()) {
global->SetNormalizedProperty(&lookup, *value);
}
} else {
function SetUpGlobal() {
%CheckIsBootstrapping();
// ECMA 262 - 15.1.1.1.
- %SetProperty(global, "NaN", $NaN, DONT_ENUM | DONT_DELETE | READ_ONLY);
+ %SetProperty(global, "NaN", $NaN, DONT_ENUM | DONT_DELETE);
// ECMA-262 - 15.1.1.2.
- %SetProperty(global, "Infinity", 1/0, DONT_ENUM | DONT_DELETE | READ_ONLY);
+ %SetProperty(global, "Infinity", 1/0, DONT_ENUM | DONT_DELETE);
// ECMA-262 - 15.1.1.3.
- %SetProperty(global, "undefined", void 0,
- DONT_ENUM | DONT_DELETE | READ_ONLY);
+ %SetProperty(global, "undefined", void 0, DONT_ENUM | DONT_DELETE);
// Set up non-enumerable function on the global object.
InstallFunctions(global, DONT_ENUM, $Array(
{ PresentPropertyContext context;
context.Check("const x; x",
- 1, // access
0,
- 2, // (re-)declaration + initialization
- EXPECT_EXCEPTION); // x is not defined!
+ 0,
+ 1, // (re-)declaration
+ EXPECT_EXCEPTION); // x has already been declared!
}
{ PresentPropertyContext context;
context.Check("const x = 0; x",
- 1, // access
0,
- 2, // (re-)declaration + initialization
- EXPECT_EXCEPTION); // x is not defined!
+ 0,
+ 1, // (re-)declaration
+ EXPECT_EXCEPTION); // x has already been declared!
}
}
{ AppearingPropertyContext context;
context.Check("const x; x",
- 1, // access
+ 0,
1, // declaration
2, // declaration + initialization
- EXPECT_RESULT, Undefined());
+ EXPECT_EXCEPTION); // x has already been declared!
}
{ AppearingPropertyContext context;
context.Check("const x = 0; x",
- 1, // access
+ 0,
1, // declaration
2, // declaration + initialization
- EXPECT_RESULT, Undefined());
- // Result is undefined because declaration succeeded but
- // initialization to 0 failed (due to context behavior).
+ EXPECT_EXCEPTION); // x has already been declared!
}
}
{ ReappearingPropertyContext context;
context.Check("const x; var x = 0",
0,
- 3, // const declaration+initialization, var initialization
+ 2, // var declaration + const initialization
4, // 2 x declaration + 2 x initialization
- EXPECT_RESULT, Undefined());
+ EXPECT_EXCEPTION); // x has already been declared!
}
}
# We are compatible with Safari and Firefox.
chapter11/11.1/11.1.5: UNIMPLEMENTED
+# We do not have a global object called 'global' as required by tests.
+chapter15/15.1: FAIL_OK
+
+# NaN is writable. We are compatible with JSC.
+chapter15/15.2/15.2.3/15.2.3.3/15.2.3.3-4-178: FAIL_OK
+# Infinity is writable. We are compatible with JSC.
+chapter15/15.2/15.2.3/15.2.3.3/15.2.3.3-4-179: FAIL_OK
+# undefined is writable. We are compatible with JSC.
+chapter15/15.2/15.2.3/15.2.3.3/15.2.3.3-4-180: FAIL_OK
+
# Our Function object has an "arguments" property which is used as a
# non-property in the test.
chapter15/15.2/15.2.3/15.2.3.3/15.2.3.3-4-183: FAIL_OK
var msg = s;
if (opt_e) { e = opt_e; msg += "; " + opt_e; }
assertEquals(expected, TestLocal(s,e), "local:'" + msg + "'");
- // Redeclarations of global consts do not throw, they are silently ignored.
- assertEquals(42, TestGlobal(s, 42), "global:'" + msg + "'");
+ assertEquals(expected, TestGlobal(s,e), "global:'" + msg + "'");
assertEquals(expected, TestContext(s,e), "context:'" + msg + "'");
}
// Test that const inside with behaves correctly.
TestAll(87, "with ({x:42}) { const x = 87; }", "x");
TestAll(undefined, "with ({x:42}) { const x; }", "x");
-
-
-// Additional tests for how various combinations of re-declarations affect
-// the values of the var/const in question.
-try {
- eval("var undefined;");
-} catch (ex) {
- assertUnreachable("undefined (1) has thrown");
-}
-
-var original_undef = undefined;
-var undefined = 1; // Should be silently ignored.
-assertEquals(original_undef, undefined, "undefined got overwritten");
-undefined = original_undef;
-
-var a; const a; const a = 1;
-assertEquals(1, a, "a has wrong value");
-a = 2;
-assertEquals(2, a, "a should be writable");
-
-var b = 1; const b = 2;
-assertEquals(2, b, "b has wrong value");
-
-var c = 1; const c = 2; const c = 3;
-assertEquals(3, c, "c has wrong value");
-
-const d = 1; const d = 2;
-assertEquals(1, d, "d has wrong value");
-
-const e = 1; var e = 2;
-assertEquals(1, e, "e has wrong value");
-
-const f = 1; const f;
-assertEquals(1, f, "f has wrong value");
-
-var g; const g = 1;
-assertEquals(1, g, "g has wrong value");
-g = 2;
-assertEquals(2, g, "g should be writable");
-
-const h; var h = 1;
-assertEquals(undefined,h, "h has wrong value");
-
-eval("Object.defineProperty(this, 'i', { writable: true });"
- + "const i = 7;"
- + "assertEquals(7, i, \"i has wrong value\");");
-
-var global = this;
-assertThrows(function() {
- Object.defineProperty(global, 'j', { writable: true })
-}, TypeError);
-const j = 2; // This is what makes the function above throw, because the
-// const declaration gets hoisted and makes the property non-configurable.
-assertEquals(2, j, "j has wrong value");
-
-var k = 1; const k;
-// You could argue about the expected result here. For now, the winning
-// argument is that "const k;" is equivalent to "const k = undefined;".
-assertEquals(undefined, k, "k has wrong value");
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Check that dynamically introducing conflicting consts/vars
-// is silently ignored (and does not lead to exceptions).
+// leads to exceptions.
var caught = 0;
try { eval("const c"); } catch (e) { caught++; assertTrue(e instanceof TypeError); }
assertTrue(typeof c == 'undefined');
try { eval("const c = 1"); } catch (e) { caught++; assertTrue(e instanceof TypeError); }
-assertEquals(1, c);
+assertTrue(typeof c == 'undefined');
eval("var d = 0");
try { eval("const d"); } catch (e) { caught++; assertTrue(e instanceof TypeError); }
-assertEquals(undefined, d);
+assertEquals(0, d);
try { eval("const d = 1"); } catch (e) { caught++; assertTrue(e instanceof TypeError); }
-assertEquals(1, d);
+assertEquals(0, d);
-assertEquals(0, caught);
+assertEquals(8, caught);
exception = true;
assertTrue(/TypeError/.test(e));
}
-assertFalse(exception);
+assertTrue(exception);
exception = false;
try {
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-// Make sure that a const definition does not try
-// to pass 'the hole' to a defined setter.
+// Make sure that a const definition always
+// conflicts with a defined setter. This avoid
+// trying to pass 'the hole' to the setter.
this.__defineSetter__('x', function(value) { assertTrue(false); });
assertTrue(e instanceof TypeError);
caught = true;
}
-assertFalse(caught);
+assertTrue(caught);
"execScript"];
CheckEcmaSemantics(this, array, "Global");
CheckReadOnlyAttr(this, "Infinity");
-CheckReadOnlyAttr(this, "NaN");
-CheckReadOnlyAttr(this, "undefined");
array = ["exec", "test", "toString", "compile"];
CheckEcmaSemantics(RegExp.prototype, array, "RegExp prototype");
assertFalse(deleted, "delete operator returned true: " + prop);
assertTrue(type.hasOwnProperty(prop), "not there after delete: " + prop);
type[prop] = "foo";
- assertEquals(old, type[prop], "overwritable: " + prop);
+ assertEquals("foo", type[prop], "overwritable: " + prop);
}
print("OK");
S15.5.4.14_A1_T3: FAIL_OK
S15.5.4.15_A1_T3: FAIL_OK
-# NaN, Infinity and undefined are read-only according to ES5.
-S15.1.1.1_A2_T1: FAIL_OK # NaN
-S15.1.1.1_A2_T2: FAIL_OK # NaN
-S15.1.1.2_A2_T1: FAIL_OK # Infinity
-# S15.1.1.2_A2_T2 would fail if it weren't bogus in r97. sputnik bug #45.
-S15.1.1.3_A2_T1: FAIL_OK # undefined
-S15.1.1.3_A2_T2: FAIL_OK # undefined
-
##################### SKIPPED TESTS #####################
# These tests take a looong time to run in debug mode.