src/jdk/internal/dynalink/beans/BeanLinker.java

changeset 1239
e1146c9cc758
parent 963
e2497b11a021
child 1490
d85f981c8cf8
equal deleted inserted replaced
1238:4dee46412516 1239:e1146c9cc758
163 MethodType.methodType(Object.class, Object.class)); 163 MethodType.methodType(Object.class, Object.class));
164 164
165 private static MethodHandle LIST_GUARD = Guards.getInstanceOfGuard(List.class); 165 private static MethodHandle LIST_GUARD = Guards.getInstanceOfGuard(List.class);
166 private static MethodHandle MAP_GUARD = Guards.getInstanceOfGuard(Map.class); 166 private static MethodHandle MAP_GUARD = Guards.getInstanceOfGuard(Map.class);
167 167
168 private enum CollectionType {
169 ARRAY, LIST, MAP
170 };
171
168 private GuardedInvocationComponent getElementGetter(final CallSiteDescriptor callSiteDescriptor, 172 private GuardedInvocationComponent getElementGetter(final CallSiteDescriptor callSiteDescriptor,
169 final LinkerServices linkerServices, final List<String> operations) throws Exception { 173 final LinkerServices linkerServices, final List<String> operations) throws Exception {
170 final MethodType callSiteType = callSiteDescriptor.getMethodType(); 174 final MethodType callSiteType = callSiteDescriptor.getMethodType();
171 final Class<?> declaredType = callSiteType.parameterType(0); 175 final Class<?> declaredType = callSiteType.parameterType(0);
172 final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(callSiteDescriptor, 176 final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(callSiteDescriptor,
176 // is, it'd be quite stupid of a call site creator to go though invokedynamic when it knows in advance they're 180 // is, it'd be quite stupid of a call site creator to go though invokedynamic when it knows in advance they're
177 // dealing with an array, or a list or map, but hey... 181 // dealing with an array, or a list or map, but hey...
178 // Note that for arrays and lists, using LinkerServices.asType() will ensure that any language specific linkers 182 // Note that for arrays and lists, using LinkerServices.asType() will ensure that any language specific linkers
179 // in use will get a chance to perform any (if there's any) implicit conversion to integer for the indices. 183 // in use will get a chance to perform any (if there's any) implicit conversion to integer for the indices.
180 final GuardedInvocationComponent gic; 184 final GuardedInvocationComponent gic;
181 final boolean isMap; 185 final CollectionType collectionType;
182 if(declaredType.isArray()) { 186 if(declaredType.isArray()) {
183 gic = new GuardedInvocationComponent(MethodHandles.arrayElementGetter(declaredType)); 187 gic = createInternalFilteredGuardedInvocationComponent(MethodHandles.arrayElementGetter(declaredType), linkerServices);
184 isMap = false; 188 collectionType = CollectionType.ARRAY;
185 } else if(List.class.isAssignableFrom(declaredType)) { 189 } else if(List.class.isAssignableFrom(declaredType)) {
186 gic = new GuardedInvocationComponent(GET_LIST_ELEMENT); 190 gic = createInternalFilteredGuardedInvocationComponent(GET_LIST_ELEMENT, linkerServices);
187 isMap = false; 191 collectionType = CollectionType.LIST;
188 } else if(Map.class.isAssignableFrom(declaredType)) { 192 } else if(Map.class.isAssignableFrom(declaredType)) {
189 gic = new GuardedInvocationComponent(GET_MAP_ELEMENT); 193 gic = createInternalFilteredGuardedInvocationComponent(GET_MAP_ELEMENT, linkerServices);
190 isMap = true; 194 collectionType = CollectionType.MAP;
191 } else if(clazz.isArray()) { 195 } else if(clazz.isArray()) {
192 gic = getClassGuardedInvocationComponent(MethodHandles.arrayElementGetter(clazz), callSiteType); 196 gic = getClassGuardedInvocationComponent(linkerServices.filterInternalObjects(MethodHandles.arrayElementGetter(clazz)), callSiteType);
193 isMap = false; 197 collectionType = CollectionType.ARRAY;
194 } else if(List.class.isAssignableFrom(clazz)) { 198 } else if(List.class.isAssignableFrom(clazz)) {
195 gic = new GuardedInvocationComponent(GET_LIST_ELEMENT, Guards.asType(LIST_GUARD, callSiteType), List.class, 199 gic = createInternalFilteredGuardedInvocationComponent(GET_LIST_ELEMENT, Guards.asType(LIST_GUARD, callSiteType), List.class, ValidationType.INSTANCE_OF,
196 ValidationType.INSTANCE_OF); 200 linkerServices);
197 isMap = false; 201 collectionType = CollectionType.LIST;
198 } else if(Map.class.isAssignableFrom(clazz)) { 202 } else if(Map.class.isAssignableFrom(clazz)) {
199 gic = new GuardedInvocationComponent(GET_MAP_ELEMENT, Guards.asType(MAP_GUARD, callSiteType), Map.class, 203 gic = createInternalFilteredGuardedInvocationComponent(GET_MAP_ELEMENT, Guards.asType(MAP_GUARD, callSiteType), Map.class, ValidationType.INSTANCE_OF,
200 ValidationType.INSTANCE_OF); 204 linkerServices);
201 isMap = true; 205 collectionType = CollectionType.MAP;
202 } else { 206 } else {
203 // Can't retrieve elements for objects that are neither arrays, nor list, nor maps. 207 // Can't retrieve elements for objects that are neither arrays, nor list, nor maps.
204 return nextComponent; 208 return nextComponent;
205 } 209 }
206 210
207 // We can have "dyn:getElem:foo", especially in composites, i.e. "dyn:getElem|getProp|getMethod:foo" 211 // We can have "dyn:getElem:foo", especially in composites, i.e. "dyn:getElem|getProp|getMethod:foo"
208 final String fixedKey = getFixedKey(callSiteDescriptor); 212 final String fixedKey = getFixedKey(callSiteDescriptor);
209 // Convert the key to a number if we're working with a list or array 213 // Convert the key to a number if we're working with a list or array
210 final Object typedFixedKey; 214 final Object typedFixedKey;
211 if(!isMap && fixedKey != null) { 215 if(collectionType != CollectionType.MAP && fixedKey != null) {
212 typedFixedKey = convertKeyToInteger(fixedKey, linkerServices); 216 typedFixedKey = convertKeyToInteger(fixedKey, linkerServices);
213 if(typedFixedKey == null) { 217 if(typedFixedKey == null) {
214 // key is not numeric, it can never succeed 218 // key is not numeric, it can never succeed
215 return nextComponent; 219 return nextComponent;
216 } 220 }
225 if(nextComponent == null) { 229 if(nextComponent == null) {
226 return gic.replaceInvocation(binder.bind(invocation)); 230 return gic.replaceInvocation(binder.bind(invocation));
227 } 231 }
228 232
229 final MethodHandle checkGuard; 233 final MethodHandle checkGuard;
230 if(invocation == GET_LIST_ELEMENT) { 234 switch(collectionType) {
235 case LIST:
231 checkGuard = convertArgToInt(RANGE_CHECK_LIST, linkerServices, callSiteDescriptor); 236 checkGuard = convertArgToInt(RANGE_CHECK_LIST, linkerServices, callSiteDescriptor);
232 } else if(invocation == GET_MAP_ELEMENT) { 237 break;
238 case MAP:
233 // TODO: A more complex solution could be devised for maps, one where we do a get() first, and fold it 239 // TODO: A more complex solution could be devised for maps, one where we do a get() first, and fold it
234 // into a GWT that tests if it returned null, and if it did, do another GWT with containsKey() 240 // into a GWT that tests if it returned null, and if it did, do another GWT with containsKey()
235 // that returns constant null (on true), or falls back to next component (on false) 241 // that returns constant null (on true), or falls back to next component (on false)
236 checkGuard = CONTAINS_MAP; 242 checkGuard = linkerServices.filterInternalObjects(CONTAINS_MAP);
237 } else { 243 break;
244 case ARRAY:
238 checkGuard = convertArgToInt(RANGE_CHECK_ARRAY, linkerServices, callSiteDescriptor); 245 checkGuard = convertArgToInt(RANGE_CHECK_ARRAY, linkerServices, callSiteDescriptor);
246 break;
247 default:
248 throw new AssertionError();
239 } 249 }
240 final MethodPair matchedInvocations = matchReturnTypes(binder.bind(invocation), 250 final MethodPair matchedInvocations = matchReturnTypes(binder.bind(invocation),
241 nextComponent.getGuardedInvocation().getInvocation()); 251 nextComponent.getGuardedInvocation().getInvocation());
242 return nextComponent.compose(matchedInvocations.guardWithTest(binder.bindTest(checkGuard)), gi.getGuard(), 252 return nextComponent.compose(matchedInvocations.guardWithTest(binder.bindTest(checkGuard)), gi.getGuard(),
243 gic.getValidatorClass(), gic.getValidationType()); 253 gic.getValidatorClass(), gic.getValidationType());
254 }
255
256 private static GuardedInvocationComponent createInternalFilteredGuardedInvocationComponent(
257 final MethodHandle invocation, final LinkerServices linkerServices) {
258 return new GuardedInvocationComponent(linkerServices.filterInternalObjects(invocation));
259 }
260
261 private static GuardedInvocationComponent createInternalFilteredGuardedInvocationComponent(
262 final MethodHandle invocation, final MethodHandle guard, final Class<?> validatorClass,
263 final ValidationType validationType, final LinkerServices linkerServices) {
264 return new GuardedInvocationComponent(linkerServices.filterInternalObjects(invocation), guard,
265 validatorClass, validationType);
244 } 266 }
245 267
246 private static String getFixedKey(final CallSiteDescriptor callSiteDescriptor) { 268 private static String getFixedKey(final CallSiteDescriptor callSiteDescriptor) {
247 return callSiteDescriptor.getNameTokenCount() == 2 ? null : callSiteDescriptor.getNameToken( 269 return callSiteDescriptor.getNameTokenCount() == 2 ? null : callSiteDescriptor.getNameToken(
248 CallSiteDescriptor.NAME_OPERAND); 270 CallSiteDescriptor.NAME_OPERAND);
379 // If declared type of receiver at the call site is already an array, a list or map, bind without guard. Thing 401 // If declared type of receiver at the call site is already an array, a list or map, bind without guard. Thing
380 // is, it'd be quite stupid of a call site creator to go though invokedynamic when it knows in advance they're 402 // is, it'd be quite stupid of a call site creator to go though invokedynamic when it knows in advance they're
381 // dealing with an array, or a list or map, but hey... 403 // dealing with an array, or a list or map, but hey...
382 // Note that for arrays and lists, using LinkerServices.asType() will ensure that any language specific linkers 404 // Note that for arrays and lists, using LinkerServices.asType() will ensure that any language specific linkers
383 // in use will get a chance to perform any (if there's any) implicit conversion to integer for the indices. 405 // in use will get a chance to perform any (if there's any) implicit conversion to integer for the indices.
384 final boolean isMap; 406 final CollectionType collectionType;
385 if(declaredType.isArray()) { 407 if(declaredType.isArray()) {
386 gic = new GuardedInvocationComponent(MethodHandles.arrayElementSetter(declaredType)); 408 gic = createInternalFilteredGuardedInvocationComponent(MethodHandles.arrayElementSetter(declaredType), linkerServices);
387 isMap = false; 409 collectionType = CollectionType.ARRAY;
388 } else if(List.class.isAssignableFrom(declaredType)) { 410 } else if(List.class.isAssignableFrom(declaredType)) {
389 gic = new GuardedInvocationComponent(SET_LIST_ELEMENT); 411 gic = createInternalFilteredGuardedInvocationComponent(SET_LIST_ELEMENT, linkerServices);
390 isMap = false; 412 collectionType = CollectionType.LIST;
391 } else if(Map.class.isAssignableFrom(declaredType)) { 413 } else if(Map.class.isAssignableFrom(declaredType)) {
392 gic = new GuardedInvocationComponent(PUT_MAP_ELEMENT); 414 gic = createInternalFilteredGuardedInvocationComponent(PUT_MAP_ELEMENT, linkerServices);
393 isMap = true; 415 collectionType = CollectionType.MAP;
394 } else if(clazz.isArray()) { 416 } else if(clazz.isArray()) {
395 gic = getClassGuardedInvocationComponent(MethodHandles.arrayElementSetter(clazz), callSiteType); 417 gic = getClassGuardedInvocationComponent(linkerServices.filterInternalObjects(
396 isMap = false; 418 MethodHandles.arrayElementSetter(clazz)), callSiteType);
419 collectionType = CollectionType.ARRAY;
397 } else if(List.class.isAssignableFrom(clazz)) { 420 } else if(List.class.isAssignableFrom(clazz)) {
398 gic = new GuardedInvocationComponent(SET_LIST_ELEMENT, Guards.asType(LIST_GUARD, callSiteType), List.class, 421 gic = createInternalFilteredGuardedInvocationComponent(SET_LIST_ELEMENT, Guards.asType(LIST_GUARD, callSiteType), List.class, ValidationType.INSTANCE_OF,
399 ValidationType.INSTANCE_OF); 422 linkerServices);
400 isMap = false; 423 collectionType = CollectionType.LIST;
401 } else if(Map.class.isAssignableFrom(clazz)) { 424 } else if(Map.class.isAssignableFrom(clazz)) {
402 gic = new GuardedInvocationComponent(PUT_MAP_ELEMENT, Guards.asType(MAP_GUARD, callSiteType), Map.class, 425 gic = createInternalFilteredGuardedInvocationComponent(PUT_MAP_ELEMENT, Guards.asType(MAP_GUARD, callSiteType),
403 ValidationType.INSTANCE_OF); 426 Map.class, ValidationType.INSTANCE_OF, linkerServices);
404 isMap = true; 427 collectionType = CollectionType.MAP;
405 } else { 428 } else {
406 // Can't set elements for objects that are neither arrays, nor list, nor maps. 429 // Can't set elements for objects that are neither arrays, nor list, nor maps.
407 gic = null; 430 gic = null;
408 isMap = false; 431 collectionType = null;
409 } 432 }
410 433
411 // In contrast to, say, getElementGetter, we only compute the nextComponent if the target object is not a map, 434 // In contrast to, say, getElementGetter, we only compute the nextComponent if the target object is not a map,
412 // as maps will always succeed in setting the element and will never need to fall back to the next component 435 // as maps will always succeed in setting the element and will never need to fall back to the next component
413 // operation. 436 // operation.
414 final GuardedInvocationComponent nextComponent = isMap ? null : getGuardedInvocationComponent( 437 final GuardedInvocationComponent nextComponent = collectionType == CollectionType.MAP ? null : getGuardedInvocationComponent(
415 callSiteDescriptor, linkerServices, operations); 438 callSiteDescriptor, linkerServices, operations);
416 if(gic == null) { 439 if(gic == null) {
417 return nextComponent; 440 return nextComponent;
418 } 441 }
419 442
420 // We can have "dyn:setElem:foo", especially in composites, i.e. "dyn:setElem|setProp:foo" 443 // We can have "dyn:setElem:foo", especially in composites, i.e. "dyn:setElem|setProp:foo"
421 final String fixedKey = getFixedKey(callSiteDescriptor); 444 final String fixedKey = getFixedKey(callSiteDescriptor);
422 // Convert the key to a number if we're working with a list or array 445 // Convert the key to a number if we're working with a list or array
423 final Object typedFixedKey; 446 final Object typedFixedKey;
424 if(!isMap && fixedKey != null) { 447 if(collectionType != CollectionType.MAP && fixedKey != null) {
425 typedFixedKey = convertKeyToInteger(fixedKey, linkerServices); 448 typedFixedKey = convertKeyToInteger(fixedKey, linkerServices);
426 if(typedFixedKey == null) { 449 if(typedFixedKey == null) {
427 // key is not numeric, it can never succeed 450 // key is not numeric, it can never succeed
428 return nextComponent; 451 return nextComponent;
429 } 452 }
437 460
438 if(nextComponent == null) { 461 if(nextComponent == null) {
439 return gic.replaceInvocation(binder.bind(invocation)); 462 return gic.replaceInvocation(binder.bind(invocation));
440 } 463 }
441 464
442 final MethodHandle checkGuard = convertArgToInt(invocation == SET_LIST_ELEMENT ? RANGE_CHECK_LIST : 465 assert collectionType == CollectionType.LIST || collectionType == CollectionType.ARRAY;
466 final MethodHandle checkGuard = convertArgToInt(collectionType == CollectionType.LIST ? RANGE_CHECK_LIST :
443 RANGE_CHECK_ARRAY, linkerServices, callSiteDescriptor); 467 RANGE_CHECK_ARRAY, linkerServices, callSiteDescriptor);
444 final MethodPair matchedInvocations = matchReturnTypes(binder.bind(invocation), 468 final MethodPair matchedInvocations = matchReturnTypes(binder.bind(invocation),
445 nextComponent.getGuardedInvocation().getInvocation()); 469 nextComponent.getGuardedInvocation().getInvocation());
446 return nextComponent.compose(matchedInvocations.guardWithTest(binder.bindTest(checkGuard)), gi.getGuard(), 470 return nextComponent.compose(matchedInvocations.guardWithTest(binder.bindTest(checkGuard)), gi.getGuard(),
447 gic.getValidatorClass(), gic.getValidationType()); 471 gic.getValidatorClass(), gic.getValidationType());

mercurial