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()); |