8139038: cleanup and documentation around JSAdapter
Reviewed-by: attila, hannesw
This commit is contained in:
parent
d12b5c154b
commit
0b4df44eb8
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -52,18 +52,18 @@ import jdk.nashorn.internal.runtime.arrays.ArrayLikeIterator;
|
|||||||
import jdk.nashorn.internal.scripts.JO;
|
import jdk.nashorn.internal.scripts.JO;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class is the implementation of the Nashorn-specific global object named {@code JSAdapter}. It can be
|
* This class is the implementation of the Nashorn-specific global object named {@code JSAdapter}. It can be thought of
|
||||||
* thought of as the {@link java.lang.reflect.Proxy} equivalent for JavaScript. NativeJSAdapter calls specially named
|
* as the {@link java.lang.reflect.Proxy} equivalent for JavaScript. A {@code NativeJSAdapter} calls specially named
|
||||||
* JavaScript methods on an adaptee object when property access/update/call/new/delete is attempted on it. Example:
|
* JavaScript methods on an adaptee object when property access/update/call/new/delete is attempted on it. Example:
|
||||||
*<pre>
|
*<pre>
|
||||||
* var y = {
|
* var y = {
|
||||||
* __get__ : function (name) { ... }
|
* __get__ : function (name) { ... }
|
||||||
* __has__ : function (name) { ... }
|
* __has__ : function (name) { ... }
|
||||||
* __put__ : function (name, value) {...}
|
* __put__ : function (name, value) {...}
|
||||||
* __call__ : function (name, arg1, arg2) {...}
|
* __call__ : function (name, arg1, arg2) {...}
|
||||||
* __new__ : function (arg1, arg2) {...}
|
* __new__ : function (arg1, arg2) {...}
|
||||||
* __delete__ : function (name) { ... }
|
* __delete__ : function (name) { ... }
|
||||||
* __getIds__ : function () { ... }
|
* __getKeys__ : function () { ... }
|
||||||
* };
|
* };
|
||||||
*
|
*
|
||||||
* var x = new JSAdapter(y);
|
* var x = new JSAdapter(y);
|
||||||
@ -74,17 +74,21 @@ import jdk.nashorn.internal.scripts.JO;
|
|||||||
* i in x; // calls y.__has__
|
* i in x; // calls y.__has__
|
||||||
* x.p = 10; // calls y.__put__
|
* x.p = 10; // calls y.__put__
|
||||||
* delete x.p; // calls y.__delete__
|
* delete x.p; // calls y.__delete__
|
||||||
* for (i in x) { print(i); } // calls y.__getIds__
|
* for (i in x) { print(i); } // calls y.__getKeys__
|
||||||
* </pre>
|
* </pre>
|
||||||
* <p>
|
* <p>
|
||||||
* JavaScript caller of adapter object is isolated from the fact that the property access/mutation/deletion are really
|
* The {@code __getKeys__} and {@code __getIds__} properties are mapped to the same operation. Concrete
|
||||||
* calls to JavaScript methods on adaptee.
|
* {@code JSAdapter} implementations are expected to use only one of these. As {@code __getIds__} exists for
|
||||||
|
* compatibility reasons only, use of {@code __getKeys__} is recommended.
|
||||||
* </p>
|
* </p>
|
||||||
* <p>
|
* <p>
|
||||||
* JSAdapter constructor can optionally receive an "overrides" object. Properties of overrides object is copied to
|
* The JavaScript caller of an adapter object is oblivious of the property access/mutation/deletion's being adapted.
|
||||||
* JSAdapter instance. When user accessed property is one of these, then adaptee's methods like {@code __get__},
|
* </p>
|
||||||
* {@code __put__} etc. are not called for those. This can be used to make certain "preferred" properties that can be
|
* <p>
|
||||||
* accessed in the usual/faster way avoiding proxy mechanism. Example:
|
* The {@code JSAdapter} constructor can optionally receive an "overrides" object. The properties of overrides object
|
||||||
|
* are copied to the {@code JSAdapter} instance. In case user-accessed properties are among these, the adaptee's methods
|
||||||
|
* like {@code __get__}, {@code __put__} etc. are not called for them. This can be used to make certain "preferred"
|
||||||
|
* properties that can be accessed in the usual/faster way avoiding the proxy mechanism. Example:
|
||||||
* </p>
|
* </p>
|
||||||
* <pre>
|
* <pre>
|
||||||
* var x = new JSAdapter({ foo: 444, bar: 6546 }) {
|
* var x = new JSAdapter({ foo: 444, bar: 6546 }) {
|
||||||
@ -95,13 +99,13 @@ import jdk.nashorn.internal.scripts.JO;
|
|||||||
* x.bar = 'hello'; // "bar" directly set without __put__ call
|
* x.bar = 'hello'; // "bar" directly set without __put__ call
|
||||||
* x.prop // calls __get__("prop") as 'prop' is not overridden
|
* x.prop // calls __get__("prop") as 'prop' is not overridden
|
||||||
* </pre>
|
* </pre>
|
||||||
* It is possible to pass a specific prototype for JSAdapter instance by passing three arguments to JSAdapter
|
* It is possible to pass a specific prototype for the {@code JSAdapter} instance by passing three arguments to the
|
||||||
* constructor. So exact signature of JSAdapter constructor is as follows:
|
* {@code JSAdapter} constructor. The exact signature of the {@code JSAdapter} constructor is as follows:
|
||||||
* <pre>
|
* <pre>
|
||||||
* JSAdapter([proto], [overrides], adaptee);
|
* JSAdapter([proto], [overrides], adaptee);
|
||||||
* </pre>
|
* </pre>
|
||||||
* Both proto and overrides are optional - but adaptee is not. When proto is not passed {@code JSAdapter.prototype} is
|
* Both the {@code proto} and {@code overrides} arguments are optional - but {@code adaptee} is not. When {@code proto}
|
||||||
* used.
|
* is not passed, {@code JSAdapter.prototype} is used.
|
||||||
*/
|
*/
|
||||||
@ScriptClass("JSAdapter")
|
@ScriptClass("JSAdapter")
|
||||||
public final class NativeJSAdapter extends ScriptObject {
|
public final class NativeJSAdapter extends ScriptObject {
|
||||||
@ -113,7 +117,7 @@ public final class NativeJSAdapter extends ScriptObject {
|
|||||||
public static final String __call__ = "__call__";
|
public static final String __call__ = "__call__";
|
||||||
/** object new operation */
|
/** object new operation */
|
||||||
public static final String __new__ = "__new__";
|
public static final String __new__ = "__new__";
|
||||||
/** object getIds operation */
|
/** object getIds operation (provided for compatibility reasons; use of getKeys is preferred) */
|
||||||
public static final String __getIds__ = "__getIds__";
|
public static final String __getIds__ = "__getIds__";
|
||||||
/** object getKeys operation */
|
/** object getKeys operation */
|
||||||
public static final String __getKeys__ = "__getKeys__";
|
public static final String __getKeys__ = "__getKeys__";
|
||||||
@ -142,7 +146,7 @@ public final class NativeJSAdapter extends ScriptObject {
|
|||||||
private final ScriptObject adaptee;
|
private final ScriptObject adaptee;
|
||||||
private final boolean overrides;
|
private final boolean overrides;
|
||||||
|
|
||||||
private static final MethodHandle IS_JSADAPTOR = findOwnMH("isJSAdaptor", boolean.class, Object.class, Object.class, MethodHandle.class, Object.class, ScriptFunction.class);
|
private static final MethodHandle IS_JSADAPTER = findOwnMH("isJSAdapter", boolean.class, Object.class, Object.class, MethodHandle.class, Object.class, ScriptFunction.class);
|
||||||
|
|
||||||
// initialized by nasgen
|
// initialized by nasgen
|
||||||
private static PropertyMap $nasgenmap$;
|
private static PropertyMap $nasgenmap$;
|
||||||
@ -626,7 +630,7 @@ public final class NativeJSAdapter extends ScriptObject {
|
|||||||
// to name. Probably not a big deal, but if we can ever make it leaner, it'd be nice.
|
// to name. Probably not a big deal, but if we can ever make it leaner, it'd be nice.
|
||||||
return new GuardedInvocation(MH.dropArguments(MH.constant(Object.class,
|
return new GuardedInvocation(MH.dropArguments(MH.constant(Object.class,
|
||||||
func.createBound(this, new Object[] { name })), 0, Object.class),
|
func.createBound(this, new Object[] { name })), 0, Object.class),
|
||||||
testJSAdaptor(adaptee, null, null, null),
|
testJSAdapter(adaptee, null, null, null),
|
||||||
adaptee.getProtoSwitchPoints(__call__, find.getOwner()), null);
|
adaptee.getProtoSwitchPoints(__call__, find.getOwner()), null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -697,7 +701,7 @@ public final class NativeJSAdapter extends ScriptObject {
|
|||||||
if (methodHandle != null) {
|
if (methodHandle != null) {
|
||||||
return new GuardedInvocation(
|
return new GuardedInvocation(
|
||||||
methodHandle,
|
methodHandle,
|
||||||
testJSAdaptor(adaptee, findData.getGetter(Object.class, INVALID_PROGRAM_POINT, null), findData.getOwner(), func),
|
testJSAdapter(adaptee, findData.getGetter(Object.class, INVALID_PROGRAM_POINT, null), findData.getOwner(), func),
|
||||||
adaptee.getProtoSwitchPoints(hook, findData.getOwner()), null);
|
adaptee.getProtoSwitchPoints(hook, findData.getOwner()), null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -710,16 +714,16 @@ public final class NativeJSAdapter extends ScriptObject {
|
|||||||
final MethodHandle methodHandle = hook.equals(__put__) ?
|
final MethodHandle methodHandle = hook.equals(__put__) ?
|
||||||
MH.asType(Lookup.EMPTY_SETTER, type) :
|
MH.asType(Lookup.EMPTY_SETTER, type) :
|
||||||
Lookup.emptyGetter(type.returnType());
|
Lookup.emptyGetter(type.returnType());
|
||||||
return new GuardedInvocation(methodHandle, testJSAdaptor(adaptee, null, null, null), adaptee.getProtoSwitchPoints(hook, null), null);
|
return new GuardedInvocation(methodHandle, testJSAdapter(adaptee, null, null, null), adaptee.getProtoSwitchPoints(hook, null), null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MethodHandle testJSAdaptor(final Object adaptee, final MethodHandle getter, final Object where, final ScriptFunction func) {
|
private static MethodHandle testJSAdapter(final Object adaptee, final MethodHandle getter, final Object where, final ScriptFunction func) {
|
||||||
return MH.insertArguments(IS_JSADAPTOR, 1, adaptee, getter, where, func);
|
return MH.insertArguments(IS_JSADAPTER, 1, adaptee, getter, where, func);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
private static boolean isJSAdaptor(final Object self, final Object adaptee, final MethodHandle getter, final Object where, final ScriptFunction func) {
|
private static boolean isJSAdapter(final Object self, final Object adaptee, final MethodHandle getter, final Object where, final ScriptFunction func) {
|
||||||
final boolean res = self instanceof NativeJSAdapter && ((NativeJSAdapter)self).getAdaptee() == adaptee;
|
final boolean res = self instanceof NativeJSAdapter && ((NativeJSAdapter)self).getAdaptee() == adaptee;
|
||||||
if (res && getter != null) {
|
if (res && getter != null) {
|
||||||
try {
|
try {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -45,8 +45,8 @@ var obj = new JSAdapter() {
|
|||||||
print("new with " + arg1 + ", " + arg2);
|
print("new with " + arg1 + ", " + arg2);
|
||||||
},
|
},
|
||||||
|
|
||||||
__getIds__: function() {
|
__getKeys__: function() {
|
||||||
print("__getIds__ called");
|
print("__getKeys__ called");
|
||||||
return [ "foo", "bar" ];
|
return [ "foo", "bar" ];
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -78,22 +78,28 @@ obj.func("hello", "world");
|
|||||||
// calls __new__
|
// calls __new__
|
||||||
new obj("hey!", "it works!");
|
new obj("hey!", "it works!");
|
||||||
|
|
||||||
|
// calls __getKeys__
|
||||||
for (i in obj) {
|
for (i in obj) {
|
||||||
print(i);
|
print(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// calls __getValues__
|
||||||
for each (i in obj) {
|
for each (i in obj) {
|
||||||
print(i);
|
print(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// calls __has__
|
||||||
var x = "foo" in obj;
|
var x = "foo" in obj;
|
||||||
print(x);
|
print(x);
|
||||||
|
|
||||||
|
// calls __has__
|
||||||
var y = "js" in obj;
|
var y = "js" in obj;
|
||||||
print(y);
|
print(y);
|
||||||
|
|
||||||
|
// calls __delete__
|
||||||
print(delete obj.prop);
|
print(delete obj.prop);
|
||||||
|
|
||||||
|
// call __get__ and __set__
|
||||||
print(obj["js"]);
|
print(obj["js"]);
|
||||||
obj["js"] = "javascript";
|
obj["js"] = "javascript";
|
||||||
print(obj["javascript"]);
|
print(obj["javascript"]);
|
||||||
|
@ -3,7 +3,7 @@ foo
|
|||||||
setter called for 'foo' with 33
|
setter called for 'foo' with 33
|
||||||
method 'func' called with hello, world
|
method 'func' called with hello, world
|
||||||
new with hey!, it works!
|
new with hey!, it works!
|
||||||
__getIds__ called
|
__getKeys__ called
|
||||||
foo
|
foo
|
||||||
bar
|
bar
|
||||||
__getValues__ called
|
__getValues__ called
|
||||||
|
Loading…
x
Reference in New Issue
Block a user