diff options
Diffstat (limited to 'src/jdk/nashorn/internal/objects/NativeObject.java')
-rw-r--r-- | src/jdk/nashorn/internal/objects/NativeObject.java | 83 |
1 files changed, 55 insertions, 28 deletions
diff --git a/src/jdk/nashorn/internal/objects/NativeObject.java b/src/jdk/nashorn/internal/objects/NativeObject.java index 4fd8aeb5..c086442d 100644 --- a/src/jdk/nashorn/internal/objects/NativeObject.java +++ b/src/jdk/nashorn/internal/objects/NativeObject.java @@ -75,7 +75,10 @@ import jdk.nashorn.internal.runtime.linker.NashornBeansLinker; */ @ScriptClass("Object") public final class NativeObject { + /** Methodhandle to proto getter */ public static final MethodHandle GET__PROTO__ = findOwnMH("get__proto__", ScriptObject.class, Object.class); + + /** Methodhandle to proto setter */ public static final MethodHandle SET__PROTO__ = findOwnMH("set__proto__", Object.class, Object.class, Object.class); private static final Object TO_STRING = new Object(); @@ -94,9 +97,7 @@ public final class NativeObject { private static ScriptObject get__proto__(final Object self) { // See ES6 draft spec: B.2.2.1.1 get Object.prototype.__proto__ // Step 1 Let O be the result of calling ToObject passing the this. - final Object obj = Global.toObject(self); - Global.checkObject(obj); - final ScriptObject sobj = (ScriptObject)obj; + final ScriptObject sobj = Global.checkObject(Global.toObject(self)); return sobj.getProto(); } @@ -282,8 +283,7 @@ public final class NativeObject { */ @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) public static ScriptObject defineProperty(final Object self, final Object obj, final Object prop, final Object attr) { - Global.checkObject(obj); - final ScriptObject sobj = (ScriptObject)obj; + final ScriptObject sobj = Global.checkObject(obj); sobj.defineOwnProperty(JSType.toString(prop), attr, true); return sobj; } @@ -298,9 +298,7 @@ public final class NativeObject { */ @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) public static ScriptObject defineProperties(final Object self, final Object obj, final Object props) { - Global.checkObject(obj); - - final ScriptObject sobj = (ScriptObject)obj; + final ScriptObject sobj = Global.checkObject(obj); final Object propsObj = Global.toObject(props); if (propsObj instanceof ScriptObject) { @@ -454,18 +452,17 @@ public final class NativeObject { */ @Constructor public static Object construct(final boolean newObj, final Object self, final Object value) { - final JSType type = JSType.of(value); + final JSType type = JSType.ofNoFunction(value); // Object(null), Object(undefined), Object() are same as "new Object()" - if (newObj || (type == JSType.NULL || type == JSType.UNDEFINED)) { + if (newObj || type == JSType.NULL || type == JSType.UNDEFINED) { switch (type) { case BOOLEAN: case NUMBER: case STRING: return Global.toObject(value); case OBJECT: - case FUNCTION: return value; case NULL: case UNDEFINED: @@ -546,7 +543,7 @@ public final class NativeObject { final Object key = JSType.toPrimitive(v, String.class); final Object obj = Global.toObject(self); - return (obj instanceof ScriptObject) && ((ScriptObject)obj).hasOwnProperty(key); + return obj instanceof ScriptObject && ((ScriptObject)obj).hasOwnProperty(key); } /** @@ -660,25 +657,29 @@ public final class NativeObject { @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) public static Object bindProperties(final Object self, final Object target, final Object source) { // target object has to be a ScriptObject - Global.checkObject(target); + final ScriptObject targetObj = Global.checkObject(target); // check null or undefined source object Global.checkObjectCoercible(source); - final ScriptObject targetObj = (ScriptObject)target; - if (source instanceof ScriptObject) { - final ScriptObject sourceObj = (ScriptObject)source; - final Property[] properties = sourceObj.getMap().getProperties(); + final ScriptObject sourceObj = (ScriptObject)source; + + final PropertyMap sourceMap = sourceObj.getMap(); + final Property[] properties = sourceMap.getProperties(); + //replace the map and blow up everything to objects to work with dual fields :-( // filter non-enumerable properties final ArrayList<Property> propList = new ArrayList<>(); - for (Property prop : properties) { + for (final Property prop : properties) { if (prop.isEnumerable()) { + final Object value = sourceObj.get(prop.getKey()); + prop.setType(Object.class); + prop.setValue(sourceObj, sourceObj, value, false); propList.add(prop); } } - if (! propList.isEmpty()) { + if (!propList.isEmpty()) { targetObj.addBoundProperties(sourceObj, propList.toArray(new Property[propList.size()])); } } else if (source instanceof ScriptObjectMirror) { @@ -696,7 +697,7 @@ public final class NativeObject { final String name = keys[idx]; final MethodHandle getter = Bootstrap.createDynamicInvoker("dyn:getMethod|getProp|getElem:" + name, MIRROR_GETTER_TYPE); final MethodHandle setter = Bootstrap.createDynamicInvoker("dyn:setProp|setElem:" + name, MIRROR_SETTER_TYPE); - props[idx] = (AccessorProperty.create(name, 0, getter, setter)); + props[idx] = AccessorProperty.create(name, 0, getter, setter); } targetObj.addBoundProperties(source, props); @@ -715,6 +716,32 @@ public final class NativeObject { return target; } + /** + * Binds the source mirror object's properties to the target object. Binding + * properties allows two-way read/write for the properties of the source object. + * All inherited, enumerable properties are also bound. This method is used to + * to make 'with' statement work with ScriptObjectMirror as scope object. + * + * @param target the target object to which the source object's properties are bound + * @param source the source object whose properties are bound to the target + * @return the target object after property binding + */ + public static Object bindAllProperties(final ScriptObject target, final ScriptObjectMirror source) { + final Set<String> keys = source.keySet(); + // make accessor properties using dynamic invoker getters and setters + final AccessorProperty[] props = new AccessorProperty[keys.size()]; + int idx = 0; + for (final String name : keys) { + final MethodHandle getter = Bootstrap.createDynamicInvoker("dyn:getMethod|getProp|getElem:" + name, MIRROR_GETTER_TYPE); + final MethodHandle setter = Bootstrap.createDynamicInvoker("dyn:setProp|setElem:" + name, MIRROR_SETTER_TYPE); + props[idx] = AccessorProperty.create(name, 0, getter, setter); + idx++; + } + + target.addBoundProperties(source, props); + return target; + } + private static void bindBeanProperties(final ScriptObject targetObj, final Object source, final Collection<String> readablePropertyNames, final Collection<String> writablePropertyNames, final Collection<String> methodNames) { @@ -772,16 +799,16 @@ public final class NativeObject { targetObj.addBoundProperties(source, properties.toArray(new AccessorProperty[properties.size()])); } - private static MethodHandle getBoundBeanMethodGetter(Object source, MethodHandle methodGetter) { + private static MethodHandle getBoundBeanMethodGetter(final Object source, final MethodHandle methodGetter) { try { // NOTE: we're relying on the fact that "dyn:getMethod:..." return value is constant for any given method // name and object linked with BeansLinker. (Actually, an even stronger assumption is true: return value is // constant for any given method name and object's class.) return MethodHandles.dropArguments(MethodHandles.constant(Object.class, - Bootstrap.bindDynamicMethod(methodGetter.invoke(source), source)), 0, Object.class); + Bootstrap.bindCallable(methodGetter.invoke(source), source, null)), 0, Object.class); } catch(RuntimeException|Error e) { throw e; - } catch(Throwable t) { + } catch(final Throwable t) { throw new RuntimeException(t); } } @@ -794,10 +821,10 @@ public final class NativeObject { assert passesGuard(source, inv.getGuard()); } catch(RuntimeException|Error e) { throw e; - } catch(Throwable t) { + } catch(final Throwable t) { throw new RuntimeException(t); } - assert inv.getSwitchPoint() == null; // Linkers in Dynalink's beans package don't use switchpoints. + assert inv.getSwitchPoints() == null; // Linkers in Dynalink's beans package don't use switchpoints. // We discard the guard, as all method handles will be bound to a specific object. return inv.getInvocation(); } @@ -806,12 +833,12 @@ public final class NativeObject { return guard == null || (boolean)guard.invoke(obj); } - private static LinkRequest createLinkRequest(String operation, MethodType methodType, Object source) { + private static LinkRequest createLinkRequest(final String operation, final MethodType methodType, final Object source) { return new LinkRequestImpl(CallSiteDescriptorFactory.create(MethodHandles.publicLookup(), operation, - methodType), false, source); + methodType), null, 0, false, source); } private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) { - return MH.findStatic(MethodHandles.lookup(), NativeObject.class, name, MH.type(rtype, types)); + return MH.findStatic(MethodHandles.lookup(), NativeObject.class, name, MH.type(rtype, types)); } } |