Commit 25b978cb authored by rossberg@chromium.org's avatar rossberg@chromium.org

This implements unscopables

The unscobables allow us to black list properties from showing up in
with statements.

https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object-environment-records-hasbinding-n

The spec draft is not fully up to date.

https://github.com/rwaldron/tc39-notes/blob/master/es6/2014-07/jul-29.md#conclusionresolution

BUG=v8:3401
LOG=Y
R=rossberg@chromium.org, verwaest@chromium.org

Review URL: https://codereview.chromium.org/384963002

Patch from Erik Arvidsson <arv@chromium.org>.

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@22942 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent a1ee057f
......@@ -253,6 +253,7 @@ action("js2c_experimental") {
"src/array-iterator.js",
"src/harmony-string.js",
"src/harmony-array.js",
"src/unscopables.js",
]
outputs = [
......
......@@ -1625,6 +1625,10 @@ void Genesis::InstallExperimentalNativeFunctions() {
INSTALL_NATIVE(JSFunction, "DerivedSetTrap", derived_set_trap);
INSTALL_NATIVE(JSFunction, "ProxyEnumerate", proxy_enumerate);
}
if (FLAG_harmony_unscopables) {
INSTALL_NATIVE(Symbol, "symbolUnscopables", unscopables_symbol);
}
}
#undef INSTALL_NATIVE
......@@ -2063,6 +2067,7 @@ bool Genesis::InstallExperimentalNatives() {
INSTALL_EXPERIMENTAL_NATIVE(i, iteration, "string-iterator.js")
INSTALL_EXPERIMENTAL_NATIVE(i, strings, "harmony-string.js")
INSTALL_EXPERIMENTAL_NATIVE(i, arrays, "harmony-array.js")
INSTALL_EXPERIMENTAL_NATIVE(i, unscopables, "unscopables.js")
}
InstallExperimentalNativeFunctions();
......
......@@ -71,6 +71,38 @@ void Context::set_global_proxy(JSObject* object) {
}
/**
* Lookups a property in an object environment, taking the unscopables into
* account. This is used For HasBinding spec algorithms for ObjectEnvironment.
*/
static Maybe<PropertyAttributes> UnscopableLookup(LookupIterator* it) {
Isolate* isolate = it->isolate();
Maybe<PropertyAttributes> attrs = JSReceiver::GetPropertyAttributes(it);
DCHECK(attrs.has_value || isolate->has_pending_exception());
if (!attrs.has_value || attrs.value == ABSENT) return attrs;
Handle<Symbol> unscopables_symbol(
isolate->native_context()->unscopables_symbol(), isolate);
Handle<Object> receiver = it->GetReceiver();
Handle<Object> unscopables;
MaybeHandle<Object> maybe_unscopables =
Object::GetProperty(receiver, unscopables_symbol);
if (!maybe_unscopables.ToHandle(&unscopables)) {
return Maybe<PropertyAttributes>();
}
if (!unscopables->IsSpecObject()) return attrs;
Maybe<bool> blacklist = JSReceiver::HasProperty(
Handle<JSReceiver>::cast(unscopables), it->name());
if (!blacklist.has_value) {
DCHECK(isolate->has_pending_exception());
return Maybe<PropertyAttributes>();
}
if (blacklist.value) return maybe(ABSENT);
return attrs;
}
Handle<Object> Context::Lookup(Handle<String> name,
ContextLookupFlags flags,
int* index,
......@@ -110,9 +142,13 @@ Handle<Object> Context::Lookup(Handle<String> name,
if ((flags & FOLLOW_PROTOTYPE_CHAIN) == 0 ||
object->IsJSContextExtensionObject()) {
maybe = JSReceiver::GetOwnPropertyAttributes(object, name);
} else if (FLAG_harmony_unscopables && context->IsWithContext()) {
LookupIterator it(object, name);
maybe = UnscopableLookup(&it);
} else {
maybe = JSReceiver::GetPropertyAttributes(object, name);
}
if (!maybe.has_value) return Handle<Object>();
DCHECK(!isolate->has_pending_exception());
*attributes = maybe.value;
......
......@@ -200,7 +200,8 @@ enum BindingFlags {
V(ITERATOR_RESULT_MAP_INDEX, Map, iterator_result_map) \
V(MAP_ITERATOR_MAP_INDEX, Map, map_iterator_map) \
V(SET_ITERATOR_MAP_INDEX, Map, set_iterator_map) \
V(ITERATOR_SYMBOL_INDEX, Symbol, iterator_symbol)
V(ITERATOR_SYMBOL_INDEX, Symbol, iterator_symbol) \
V(UNSCOPABLES_SYMBOL_INDEX, Symbol, unscopables_symbol)
// JSFunctions are pairs (context, function code), sometimes also called
// closures. A Context object is used to represent function contexts and
......@@ -394,6 +395,7 @@ class Context: public FixedArray {
MAP_ITERATOR_MAP_INDEX,
SET_ITERATOR_MAP_INDEX,
ITERATOR_SYMBOL_INDEX,
UNSCOPABLES_SYMBOL_INDEX,
// Properties from here are treated as weak references by the full GC.
// Scavenge treats them as strong references.
......
......@@ -161,6 +161,7 @@ DEFINE_BOOL(harmony_numeric_literals, false,
DEFINE_BOOL(harmony_strings, false, "enable harmony string")
DEFINE_BOOL(harmony_arrays, false, "enable harmony arrays")
DEFINE_BOOL(harmony_arrow_functions, false, "enable harmony arrow functions")
DEFINE_BOOL(harmony_unscopables, false, "enable harmony unscopables")
DEFINE_BOOL(harmony, false, "enable all harmony features")
DEFINE_IMPLICATION(harmony, harmony_scoping)
......
......@@ -105,7 +105,7 @@ function SetUpSymbol() {
// "isRegExp", symbolIsRegExp,
"iterator", symbolIterator
// "toStringTag", symbolToStringTag,
// "unscopables", symbolUnscopables
// "unscopables", symbolUnscopables // added in unscopables.js
));
InstallFunctions($Symbol, DONT_ENUM, $Array(
"for", SymbolFor,
......
// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
'use strict';
// This file relies on the fact that the following declaration has been made.
// var $Array = global.Array;
// var $Symbol = global.Symbol;
function ExtendSymbol() {
%CheckIsBootstrapping();
InstallConstants($Symbol, $Array(
"unscopables", symbolUnscopables
));
}
ExtendSymbol();
var arrayUnscopables = {
__proto__: null,
copyWithin: true,
entries: true,
fill: true,
find: true,
findIndex: true,
keys: true,
values: true,
};
function ExtendArrayPrototype() {
%CheckIsBootstrapping();
%AddNamedProperty($Array.prototype, symbolUnscopables, arrayUnscopables,
DONT_ENUM | READ_ONLY);
}
ExtendArrayPrototype();
......@@ -156,6 +156,7 @@
'test-types.cc',
'test-unbound-queue.cc',
'test-unique.cc',
'test-unscopables-hidden-prototype.cc',
'test-utils.cc',
'test-version.cc',
'test-weakmaps.cc',
......
// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stdlib.h>
#include "src/v8.h"
#include "test/cctest/cctest.h"
namespace {
static void Cleanup() {
CompileRun(
"delete object.x;"
"delete hidden_prototype.x;"
"delete object[Symbol.unscopables];"
"delete hidden_prototype[Symbol.unscopables];");
}
TEST(Unscopables) {
i::FLAG_harmony_unscopables = true;
LocalContext context;
v8::Isolate* isolate = context->GetIsolate();
v8::HandleScope handle_scope(isolate);
v8::Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New(isolate);
v8::Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New(isolate);
t1->SetHiddenPrototype(true);
v8::Local<v8::Object> object = t0->GetFunction()->NewInstance();
v8::Local<v8::Object> hidden_prototype = t1->GetFunction()->NewInstance();
object->SetPrototype(hidden_prototype);
context->Global()->Set(v8_str("object"), object);
context->Global()->Set(v8_str("hidden_prototype"), hidden_prototype);
CHECK_EQ(1, CompileRun(
"var result;"
"var x = 0;"
"object.x = 1;"
"with (object) {"
" result = x;"
"}"
"result")->Int32Value());
Cleanup();
CHECK_EQ(2, CompileRun(
"var result;"
"var x = 0;"
"hidden_prototype.x = 2;"
"with (object) {"
" result = x;"
"}"
"result")->Int32Value());
Cleanup();
CHECK_EQ(0, CompileRun(
"var result;"
"var x = 0;"
"object.x = 3;"
"object[Symbol.unscopables] = {x: true};"
"with (object) {"
" result = x;"
"}"
"result")->Int32Value());
Cleanup();
CHECK_EQ(0, CompileRun(
"var result;"
"var x = 0;"
"hidden_prototype.x = 4;"
"hidden_prototype[Symbol.unscopables] = {x: true};"
"with (object) {"
" result = x;"
"}"
"result")->Int32Value());
Cleanup();
CHECK_EQ(0, CompileRun(
"var result;"
"var x = 0;"
"object.x = 5;"
"hidden_prototype[Symbol.unscopables] = {x: true};"
"with (object) {"
" result = x;"
"}"
"result;")->Int32Value());
Cleanup();
CHECK_EQ(0, CompileRun(
"var result;"
"var x = 0;"
"hidden_prototype.x = 6;"
"object[Symbol.unscopables] = {x: true};"
"with (object) {"
" result = x;"
"}"
"result")->Int32Value());
}
}
// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --harmony-unscopables
// Flags: --harmony-proxies
// TODO(arv): Once proxies can intercept symbols, add more tests.
function TestBasics() {
var log = [];
var proxy = Proxy.create({
getPropertyDescriptor: function(key) {
log.push(key);
if (key === 'x') {
return {
value: 1,
configurable: true
};
}
return undefined;
}
});
var x = 'local';
with (proxy) {
assertEquals(1, x);
}
// One 'x' for HasBinding and one for GetBindingValue
assertEquals(['assertEquals', 'x', 'x'], log);
}
TestBasics();
function TestInconsistent() {
var log = [];
var calls = 0;
var proxy = Proxy.create({
getPropertyDescriptor: function(key) {
log.push(key);
if (key === 'x' && calls < 1) {
calls++;
return {
value: 1,
configurable: true
};
}
return undefined;
}
});
var x = 'local';
with (proxy) {
assertEquals(void 0, x);
}
// One 'x' for HasBinding and one for GetBindingValue
assertEquals(['assertEquals', 'x', 'x'], log);
}
TestInconsistent();
function TestUseProxyAsUnscopables() {
var x = 1;
var object = {
x: 2
};
var calls = 0;
var proxy = Proxy.create({
has: function(key) {
calls++;
assertEquals('x', key);
return calls === 2;
},
getPropertyDescriptor: function(key) {
assertUnreachable();
}
});
object[Symbol.unscopables] = proxy;
with (object) {
assertEquals(2, x);
assertEquals(1, x);
}
// HasBinding, HasBinding
assertEquals(2, calls);
}
TestUseProxyAsUnscopables();
function TestThrowInHasUnscopables() {
var x = 1;
var object = {
x: 2
};
function CustomError() {}
var calls = 0;
var proxy = Proxy.create({
has: function(key) {
if (calls++ === 0) {
throw new CustomError();
}
assertUnreachable();
},
getPropertyDescriptor: function(key) {
assertUnreachable();
}
});
object[Symbol.unscopables] = proxy;
assertThrows(function() {
with (object) {
x;
}
}, CustomError);
}
TestThrowInHasUnscopables();
var global = this;
function TestGlobalShouldIgnoreUnscopables() {
global.x = 1;
var proxy = Proxy.create({
getPropertyDescriptor: function() {
assertUnreachable();
}
});
global[Symbol.unscopables] = proxy;
assertEquals(1, global.x);
assertEquals(1, x);
global.x = 2;
assertEquals(2, global.x);
assertEquals(2, x);
x = 3;
assertEquals(3, global.x);
assertEquals(3, x);
}
TestGlobalShouldIgnoreUnscopables();
This diff is collapsed.
......@@ -51,7 +51,7 @@ EXPECTED_FUNCTION_COUNT = 427
EXPECTED_FUZZABLE_COUNT = 330
EXPECTED_CCTEST_COUNT = 7
EXPECTED_UNKNOWN_COUNT = 16
EXPECTED_BUILTINS_COUNT = 809
EXPECTED_BUILTINS_COUNT = 811
# Don't call these at all.
......
......@@ -1417,6 +1417,7 @@
'../../src/string-iterator.js',
'../../src/harmony-string.js',
'../../src/harmony-array.js',
'../../src/unscopables.js',
],
'libraries_bin_file': '<(SHARED_INTERMEDIATE_DIR)/libraries.bin',
'libraries_experimental_bin_file': '<(SHARED_INTERMEDIATE_DIR)/libraries-experimental.bin',
......
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