From 8bfb7189a3472bc9d0820a1bd4534eaaf78ff847 Mon Sep 17 00:00:00 2001 From: cbruni Date: Mon, 21 Dec 2015 04:47:15 -0800 Subject: [PATCH] [proxies] Better print for proxies in d8 Function proxies would not be printed so far since they ended up in Function.prototype.toString which only works with Function as a receiver but no Proxy. Additionally added support for more gracefully dealing with recursive __proto__ structures introduced by proxies. BUG=v8:1543 LOG=n Review URL: https://codereview.chromium.org/1530293004 Cr-Commit-Position: refs/heads/master@{#32985} --- src/d8.js | 81 ++++++++++++++++++++++-------------- src/js/collection.js | 2 +- src/js/json.js | 2 +- src/js/macros.py | 47 +++++++++++---------- src/js/messages.js | 2 +- src/js/object-observe.js | 2 +- src/js/proxy.js | 2 +- src/js/v8natives.js | 8 ++-- src/runtime/runtime-proxy.cc | 12 +++++- src/runtime/runtime.h | 5 ++- 10 files changed, 96 insertions(+), 67 deletions(-) diff --git a/src/d8.js b/src/d8.js index 8d55c788e2..c375d20698 100644 --- a/src/d8.js +++ b/src/d8.js @@ -13,6 +13,9 @@ function Stringify(x, depth) { depth = stringifyDepthLimit; else if (depth === 0) return "*"; + if (IS_PROXY(x)) { + return StringifyProxy(x, depth); + } switch (typeof x) { case "undefined": return "undefined"; @@ -26,40 +29,56 @@ function Stringify(x, depth) { return x.toString(); case "object": if (IS_NULL(x)) return "null"; - if (x.constructor && x.constructor.name === "Array") { - var elems = []; - for (var i = 0; i < x.length; ++i) { - elems.push( - {}.hasOwnProperty.call(x, i) ? Stringify(x[i], depth - 1) : ""); - } - return "[" + elems.join(", ") + "]"; - } try { - var string = String(x); - if (string && string !== "[object Object]") return string; - } catch(e) {} - var props = []; - var names = Object.getOwnPropertyNames(x); - names = names.concat(Object.getOwnPropertySymbols(x)); - for (var i in names) { - var name = names[i]; - var desc = Object.getOwnPropertyDescriptor(x, name); - if (IS_UNDEFINED(desc)) continue; - if (IS_SYMBOL(name)) name = "[" + Stringify(name) + "]"; - if ("value" in desc) { - props.push(name + ": " + Stringify(desc.value, depth - 1)); - } - if (desc.get) { - var getter = Stringify(desc.get); - props.push("get " + name + getter.slice(getter.indexOf('('))); - } - if (desc.set) { - var setter = Stringify(desc.set); - props.push("set " + name + setter.slice(setter.indexOf('('))); - } + return StringifyObject(x, depth); + } catch(RangeError) { + return "{*}" } - return "{" + props.join(", ") + "}"; default: return "[crazy non-standard value]"; } } + +function StringifyObject(x, depth) { + if (x.constructor && x.constructor.name === "Array") { + var elems = []; + for (var i = 0; i < x.length; ++i) { + elems.push( + {}.hasOwnProperty.call(x, i) ? Stringify(x[i], depth - 1) : ""); + } + return "[" + elems.join(", ") + "]"; + } + try { + var string = String(x); + if (string && string !== "[object Object]") return string; + } catch(e) {} + var props = []; + var names = Object.getOwnPropertyNames(x); + names = names.concat(Object.getOwnPropertySymbols(x)); + for (var i in names) { + var name = names[i]; + var desc = Object.getOwnPropertyDescriptor(x, name); + if (IS_UNDEFINED(desc)) continue; + if (IS_SYMBOL(name)) name = "[" + Stringify(name) + "]"; + if ("value" in desc) { + props.push(name + ": " + Stringify(desc.value, depth - 1)); + } + if (desc.get) { + var getter = Stringify(desc.get); + props.push("get " + name + getter.slice(getter.indexOf('('))); + } + if (desc.set) { + var setter = Stringify(desc.set); + props.push("set " + name + setter.slice(setter.indexOf('('))); + } + } + return "{" + props.join(", ") + "}"; +} + +function StringifyProxy(proxy, depth) { + var proxy_type = typeof proxy; + return '[' + proxy_type + ' Proxy ' + Stringify({ + target: %JSProxyGetTarget(proxy), + handler: %JSProxyGetHandler(proxy) + }, depth-1) + ']'; +} diff --git a/src/js/collection.js b/src/js/collection.js index 10d8a385e5..0d7195d53e 100644 --- a/src/js/collection.js +++ b/src/js/collection.js @@ -100,7 +100,7 @@ function GetExistingHash(key) { if ((field & 1 /* Name::kHashNotComputedMask */) === 0) { return field >>> 2 /* Name::kHashShift */; } - } else if (IS_RECEIVER(key) && !%_IsJSProxy(key) && !IS_GLOBAL(key)) { + } else if (IS_RECEIVER(key) && !IS_PROXY(key) && !IS_GLOBAL(key)) { var hash = GET_PRIVATE(key, hashCodeSymbol); return hash; } diff --git a/src/js/json.js b/src/js/json.js index ddda1be7e2..45c6792fd0 100644 --- a/src/js/json.js +++ b/src/js/json.js @@ -188,7 +188,7 @@ function JSONSerialize(key, holder, replacer, stack, indent, gap) { function JSONStringify(value, replacer, space) { - if (%_ArgumentsLength() == 1 && !%_IsJSProxy(value)) { + if (%_ArgumentsLength() == 1 && !IS_PROXY(value)) { return %BasicJSONStringify(value); } if (!IS_CALLABLE(replacer) && %is_arraylike(replacer)) { diff --git a/src/js/macros.py b/src/js/macros.py index d7deabb3df..d92e2acc76 100644 --- a/src/js/macros.py +++ b/src/js/macros.py @@ -88,37 +88,38 @@ define STRING_TO_REGEXP_CACHE_ID = 0; # Note: We have special support for typeof(foo) === 'bar' in the compiler. # It will *not* generate a runtime typeof call for the most important # values of 'bar'. +macro IS_ARRAY(arg) = (%_IsArray(arg)); +macro IS_ARRAYBUFFER(arg) = (%_ClassOf(arg) === 'ArrayBuffer'); +macro IS_BOOLEAN(arg) = (typeof(arg) === 'boolean'); +macro IS_BOOLEAN_WRAPPER(arg) = (%_ClassOf(arg) === 'Boolean'); +macro IS_DATAVIEW(arg) = (%_ClassOf(arg) === 'DataView'); +macro IS_DATE(arg) = (%_IsDate(arg)); +macro IS_ERROR(arg) = (%_ClassOf(arg) === 'Error'); +macro IS_FUNCTION(arg) = (%_IsFunction(arg)); +macro IS_GENERATOR(arg) = (%_ClassOf(arg) === 'Generator'); +macro IS_GLOBAL(arg) = (%_ClassOf(arg) === 'global'); +macro IS_MAP(arg) = (%_ClassOf(arg) === 'Map'); +macro IS_MAP_ITERATOR(arg) = (%_ClassOf(arg) === 'Map Iterator'); macro IS_NULL(arg) = (arg === null); macro IS_NULL_OR_UNDEFINED(arg) = (arg == null); -macro IS_UNDEFINED(arg) = (arg === (void 0)); macro IS_NUMBER(arg) = (typeof(arg) === 'number'); -macro IS_STRING(arg) = (typeof(arg) === 'string'); -macro IS_BOOLEAN(arg) = (typeof(arg) === 'boolean'); -macro IS_SYMBOL(arg) = (typeof(arg) === 'symbol'); +macro IS_NUMBER_WRAPPER(arg) = (%_ClassOf(arg) === 'Number'); macro IS_OBJECT(arg) = (typeof(arg) === 'object'); -macro IS_ARRAY(arg) = (%_IsArray(arg)); -macro IS_DATE(arg) = (%_IsDate(arg)); -macro IS_FUNCTION(arg) = (%_IsFunction(arg)); +macro IS_PROXY(arg) = (%_IsJSProxy(arg)); macro IS_REGEXP(arg) = (%_IsRegExp(arg)); -macro IS_SIMD_VALUE(arg) = (%_IsSimdValue(arg)); +macro IS_SCRIPT(arg) = (%_ClassOf(arg) === 'Script'); macro IS_SET(arg) = (%_ClassOf(arg) === 'Set'); -macro IS_MAP(arg) = (%_ClassOf(arg) === 'Map'); +macro IS_SET_ITERATOR(arg) = (%_ClassOf(arg) === 'Set Iterator'); +macro IS_SHAREDARRAYBUFFER(arg) = (%_ClassOf(arg) === 'SharedArrayBuffer'); +macro IS_SIMD_VALUE(arg) = (%_IsSimdValue(arg)); +macro IS_STRING(arg) = (typeof(arg) === 'string'); +macro IS_STRING_WRAPPER(arg) = (%_ClassOf(arg) === 'String'); +macro IS_STRONG(arg) = (%IsStrong(arg)); +macro IS_SYMBOL(arg) = (typeof(arg) === 'symbol'); +macro IS_SYMBOL_WRAPPER(arg) = (%_ClassOf(arg) === 'Symbol'); +macro IS_UNDEFINED(arg) = (arg === (void 0)); macro IS_WEAKMAP(arg) = (%_ClassOf(arg) === 'WeakMap'); macro IS_WEAKSET(arg) = (%_ClassOf(arg) === 'WeakSet'); -macro IS_NUMBER_WRAPPER(arg) = (%_ClassOf(arg) === 'Number'); -macro IS_STRING_WRAPPER(arg) = (%_ClassOf(arg) === 'String'); -macro IS_SYMBOL_WRAPPER(arg) = (%_ClassOf(arg) === 'Symbol'); -macro IS_BOOLEAN_WRAPPER(arg) = (%_ClassOf(arg) === 'Boolean'); -macro IS_ERROR(arg) = (%_ClassOf(arg) === 'Error'); -macro IS_SCRIPT(arg) = (%_ClassOf(arg) === 'Script'); -macro IS_GLOBAL(arg) = (%_ClassOf(arg) === 'global'); -macro IS_ARRAYBUFFER(arg) = (%_ClassOf(arg) === 'ArrayBuffer'); -macro IS_DATAVIEW(arg) = (%_ClassOf(arg) === 'DataView'); -macro IS_SHAREDARRAYBUFFER(arg) = (%_ClassOf(arg) === 'SharedArrayBuffer'); -macro IS_GENERATOR(arg) = (%_ClassOf(arg) === 'Generator'); -macro IS_SET_ITERATOR(arg) = (%_ClassOf(arg) === 'Set Iterator'); -macro IS_MAP_ITERATOR(arg) = (%_ClassOf(arg) === 'Map Iterator'); -macro IS_STRONG(arg) = (%IsStrong(arg)); # Macro for ES queries of the type: "Type(O) is Object." macro IS_RECEIVER(arg) = (%_IsJSReceiver(arg)); diff --git a/src/js/messages.js b/src/js/messages.js index 3bcbfe934f..8016c4d5ee 100644 --- a/src/js/messages.js +++ b/src/js/messages.js @@ -843,7 +843,7 @@ function FormatStackTrace(obj, raw_stack) { function GetTypeName(receiver, requireConstructor) { if (IS_NULL_OR_UNDEFINED(receiver)) return null; - if (%_IsJSProxy(receiver)) return "Proxy"; + if (IS_PROXY(receiver)) return "Proxy"; var constructor = %GetDataProperty(TO_OBJECT(receiver), "constructor"); if (!IS_FUNCTION(constructor)) { diff --git a/src/js/object-observe.js b/src/js/object-observe.js index 2438346496..2de71aa19d 100644 --- a/src/js/object-observe.js +++ b/src/js/object-observe.js @@ -192,7 +192,7 @@ function ObserverIsActive(observer, objectInfo) { function ObjectInfoGetOrCreate(object) { var objectInfo = ObjectInfoGet(object); if (IS_UNDEFINED(objectInfo)) { - if (!%_IsJSProxy(object)) { + if (!IS_PROXY(object)) { %SetIsObserved(object); } objectInfo = { diff --git a/src/js/proxy.js b/src/js/proxy.js index b859415f06..842bac0252 100644 --- a/src/js/proxy.js +++ b/src/js/proxy.js @@ -22,7 +22,7 @@ utils.Import(function(from) { function ProxyCreateRevocable(target, handler) { var p = new GlobalProxy(target, handler); - return {proxy: p, revoke: () => %RevokeProxy(p)}; + return {proxy: p, revoke: () => %JSProxyRevoke(p)}; } // ------------------------------------------------------------------- diff --git a/src/js/v8natives.js b/src/js/v8natives.js index be10a105ae..a394629c1b 100644 --- a/src/js/v8natives.js +++ b/src/js/v8natives.js @@ -500,11 +500,11 @@ function CallTrap2(handler, name, defaultTrap, x, y) { // ObjectGetOwnPropertyDescriptor and delete this. function GetOwnPropertyJS(obj, v) { var p = TO_NAME(v); - if (%_IsJSProxy(obj)) { + if (IS_PROXY(obj)) { // TODO(rossberg): adjust once there is a story for symbols vs proxies. if (IS_SYMBOL(v)) return UNDEFINED; - var handler = %GetHandler(obj); + var handler = %JSProxyGetHandler(obj); var descriptor = CallTrap1( handler, "getOwnPropertyDescriptor", UNDEFINED, p); if (IS_UNDEFINED(descriptor)) return descriptor; @@ -538,7 +538,7 @@ function DefineProxyProperty(obj, p, attributes, should_throw) { // TODO(rossberg): adjust once there is a story for symbols vs proxies. if (IS_SYMBOL(p)) return false; - var handler = %GetHandler(obj); + var handler = %JSProxyGetHandler(obj); var result = CallTrap2(handler, "defineProperty", UNDEFINED, p, attributes); if (!result) { if (should_throw) { @@ -761,7 +761,7 @@ function DefineArrayProperty(obj, p, desc, should_throw) { // ES5 section 8.12.9, ES5 section 15.4.5.1 and Harmony proxies. function DefineOwnProperty(obj, p, desc, should_throw) { - if (%_IsJSProxy(obj)) { + if (IS_PROXY(obj)) { // TODO(rossberg): adjust once there is a story for symbols vs proxies. if (IS_SYMBOL(p)) return false; diff --git a/src/runtime/runtime-proxy.cc b/src/runtime/runtime-proxy.cc index 6980b88cae..3a521c6b7c 100644 --- a/src/runtime/runtime-proxy.cc +++ b/src/runtime/runtime-proxy.cc @@ -149,7 +149,7 @@ RUNTIME_FUNCTION(Runtime_IsJSProxy) { } -RUNTIME_FUNCTION(Runtime_GetHandler) { +RUNTIME_FUNCTION(Runtime_JSProxyGetHandler) { SealHandleScope shs(isolate); DCHECK(args.length() == 1); CONVERT_ARG_CHECKED(JSProxy, proxy, 0); @@ -157,7 +157,15 @@ RUNTIME_FUNCTION(Runtime_GetHandler) { } -RUNTIME_FUNCTION(Runtime_RevokeProxy) { +RUNTIME_FUNCTION(Runtime_JSProxyGetTarget) { + SealHandleScope shs(isolate); + DCHECK(args.length() == 1); + CONVERT_ARG_CHECKED(JSProxy, proxy, 0); + return proxy->target(); +} + + +RUNTIME_FUNCTION(Runtime_JSProxyRevoke) { HandleScope scope(isolate); DCHECK(args.length() == 1); CONVERT_ARG_HANDLE_CHECKED(JSProxy, proxy, 0); diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h index 6a8dc94ec4..8119138bb9 100644 --- a/src/runtime/runtime.h +++ b/src/runtime/runtime.h @@ -543,8 +543,9 @@ namespace internal { F(IsJSProxy, 1, 1) \ F(JSProxyCall, -1 /* >= 2 */, 1) \ F(JSProxyConstruct, -1 /* >= 3 */, 1) \ - F(GetHandler, 1, 1) \ - F(RevokeProxy, 1, 1) + F(JSProxyGetTarget, 1, 1) \ + F(JSProxyGetHandler, 1, 1) \ + F(JSProxyRevoke, 1, 1) #define FOR_EACH_INTRINSIC_REGEXP(F) \ F(StringReplaceGlobalRegExpWithString, 4, 1) \