use of org.jenkinsci.plugins.structs.describable.UninstantiatedDescribable in project workflow-cps-plugin by jenkinsci.
the class ArgumentsActionImpl method sanitizeObjectAndRecordMutation.
/**
* Recursively sanitize a single object by:
* - Exploding {@link Step}s and {@link UninstantiatedDescribable}s into their Maps to sanitize
* - Removing unsafe strings using {@link #isStringSafe(String, EnvVars, Set)} and replace with {@link NotStoredReason#MASKED_VALUE}
* - Removing oversized objects using {@link #isOversized(Object)} and replacing with {@link NotStoredReason#OVERSIZE_VALUE}
* While making an effort not to retain needless copies of objects and to re-use originals where possible
* (including the Step or UninstantiatedDescribable)
*/
@CheckForNull
@SuppressWarnings("unchecked")
Object sanitizeObjectAndRecordMutation(@CheckForNull Object o, @CheckForNull EnvVars vars) {
// Package scoped so we can test it directly
Object tempVal = o;
if (tempVal instanceof Step) {
// Ugly but functional used for legacy syntaxes with metasteps
tempVal = ((Step) tempVal).getDescriptor().defineArguments((Step) tempVal);
} else if (tempVal instanceof UninstantiatedDescribable) {
tempVal = ((UninstantiatedDescribable) tempVal).toMap();
}
if (isOversized(tempVal)) {
this.isUnmodifiedBySanitization = false;
return NotStoredReason.OVERSIZE_VALUE;
}
Object modded = tempVal;
if (modded instanceof Map) {
// Recursive sanitization, oh my!
modded = sanitizeMapAndRecordMutation((Map) modded, vars);
} else if (modded instanceof List) {
modded = sanitizeListAndRecordMutation((List) modded, vars);
} else if (modded instanceof String && vars != null && !vars.isEmpty() && !isStringSafe((String) modded, vars, SAFE_ENVIRONMENT_VARIABLES)) {
this.isUnmodifiedBySanitization = false;
return NotStoredReason.MASKED_VALUE;
}
if (modded != tempVal) {
// Sanitization stripped out some values, so we need to record that and return modified version
this.isUnmodifiedBySanitization = false;
if (o instanceof UninstantiatedDescribable) {
// Need to retain the symbol.
UninstantiatedDescribable ud = (UninstantiatedDescribable) o;
return new UninstantiatedDescribable(ud.getSymbol(), ud.getKlass(), (Map<String, ?>) modded);
} else {
return modded;
}
} else {
// Any mutation was just from exploding step/uninstantiated describable, and we can just use the original
return o;
}
}
use of org.jenkinsci.plugins.structs.describable.UninstantiatedDescribable in project workflow-cps-plugin by jenkinsci.
the class DSL method invokeDescribable.
/**
* When {@link #invokeMethod(String, Object)} is calling a generic {@link Descriptor}
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
protected Object invokeDescribable(String symbol, Object _args) {
List<StepDescriptor> metaSteps = StepDescriptor.metaStepsOf(symbol);
StepDescriptor metaStep = metaSteps.size() == 1 ? metaSteps.get(0) : null;
boolean singleArgumentOnly = false;
if (metaStep != null) {
Descriptor symbolDescriptor = SymbolLookup.get().findDescriptor((Class) (metaStep.getMetaStepArgumentType()), symbol);
DescribableModel<?> symbolModel = DescribableModel.of(symbolDescriptor.clazz);
singleArgumentOnly = symbolModel.hasSingleRequiredParameter() && symbolModel.getParameters().size() == 1;
}
// The only time a closure is valid is when the resulting Describable is immediately executed via a meta-step
NamedArgsAndClosure args = parseArgs(_args, metaStep != null && metaStep.takesImplicitBlockArgument(), UninstantiatedDescribable.ANONYMOUS_KEY, singleArgumentOnly);
UninstantiatedDescribable ud = new UninstantiatedDescribable(symbol, null, args.namedArgs);
if (metaStep == null) {
// might be resolved with a specific type.
return ud;
} else {
Descriptor d = SymbolLookup.get().findDescriptor((Class) (metaStep.getMetaStepArgumentType()), symbol);
try {
// execute this Describable through a meta-step
// split args between MetaStep (represented by mm) and Describable (represented by dm)
DescribableModel<?> mm = DescribableModel.of(metaStep.clazz);
DescribableModel<?> dm = DescribableModel.of(d.clazz);
DescribableParameter p = mm.getFirstRequiredParameter();
if (p == null) {
// meta-step not having a required parameter is a bug in this meta step
throw new IllegalArgumentException("Attempted to use meta-step " + metaStep.getFunctionName() + " to process " + symbol + " but this meta-step is buggy; it has no mandatory parameter");
}
// order of preference:
// 1. mandatory parameter in mm
// 2. mandatory parameter in dm
// 3. other parameters in mm
// 4. other parameters in dm
// mm is preferred over dm because that way at least the arguments that mm defines
// act consistently
Map<String, Object> margs = new TreeMap<>();
Map<String, Object> dargs = new TreeMap<>();
for (Entry<String, ?> e : ud.getArguments().entrySet()) {
String n = e.getKey();
Object v = e.getValue();
DescribableParameter mp = mm.getParameter(n);
DescribableParameter dp = dm.getParameter(n);
if (mp != null && mp.isRequired()) {
margs.put(n, v);
} else if (dp != null && dp.isRequired()) {
dargs.put(n, v);
} else if (mp != null) {
margs.put(n, v);
} else {
// dp might be null, but this error will be caught by UD.instantiate() later
dargs.put(n, v);
}
}
ud = new UninstantiatedDescribable(symbol, null, dargs);
margs.put(p.getName(), ud);
return invokeStep(metaStep, new NamedArgsAndClosure(margs, args.body));
} catch (Exception e) {
throw new IllegalArgumentException("Failed to prepare " + symbol + " step", e);
}
}
}
use of org.jenkinsci.plugins.structs.describable.UninstantiatedDescribable in project workflow-cps-plugin by jenkinsci.
the class Snippetizer method object2Groovy.
/**
* Renders the invocation syntax to re-create a given object 'o' into 'b'
*
* @param nestedExp
* true if this object is written as a nested expression (in which case we always produce parenthesis for readability)
* @return the same object as 'b'
*/
static StringBuilder object2Groovy(StringBuilder b, Object o, boolean nestedExp) throws UnsupportedOperationException {
if (o == null) {
return b.append("null");
}
final Class<?> clazz = o.getClass();
if (clazz == String.class || clazz == Character.class) {
String text = String.valueOf(o);
if (text.contains("\n")) {
b.append("'''").append(text.replace("\\", "\\\\").replace("'", "\\'")).append("'''");
} else {
b.append('\'').append(text.replace("\\", "\\\\").replace("'", "\\'")).append('\'');
}
return b;
}
if (clazz == Boolean.class || clazz == Integer.class || clazz == Long.class || clazz == Float.class || clazz == Double.class || clazz == Byte.class || clazz == Short.class) {
return b.append(o);
}
if (o instanceof List) {
return list2groovy(b, (List<?>) o);
}
if (o instanceof Map) {
return map2groovy(b, (Map) o);
}
if (o instanceof UninstantiatedDescribable) {
return ud2groovy(b, (UninstantiatedDescribable) o, false, nestedExp);
}
for (StepDescriptor d : StepDescriptor.all()) {
if (d.clazz.equals(clazz)) {
Step step = (Step) o;
UninstantiatedDescribable uninst = d.uninstantiate(step);
boolean blockArgument = d.takesImplicitBlockArgument();
if (d.isMetaStep()) {
// if we have a symbol name for the wrapped Describable, we can produce
// a more concise form that hides it
DescribableModel<?> m = DescribableModel.of(d.clazz);
DescribableParameter p = m.getFirstRequiredParameter();
if (p != null) {
Object wrapped = uninst.getArguments().get(p.getName());
if (wrapped instanceof UninstantiatedDescribable) {
// if we cannot represent this 'o' in a concise syntax that hides meta-step, set this to true
boolean failSimplification = false;
UninstantiatedDescribable nested = (UninstantiatedDescribable) wrapped;
TreeMap<String, Object> copy = new TreeMap<String, Object>(nested.getArguments());
for (Entry<String, ?> e : uninst.getArguments().entrySet()) {
if (!e.getKey().equals(p.getName())) {
if (copy.put(e.getKey(), e.getValue()) != null) {
// collision between a parameter in meta-step and wrapped-step,
// which cannot be reconciled unless we explicitly write out
// meta-step
failSimplification = true;
}
}
}
if (!canUseMetaStep(nested))
failSimplification = true;
if (!failSimplification) {
// write out in a short-form
UninstantiatedDescribable combined = new UninstantiatedDescribable(nested.getSymbol(), nested.getKlass(), copy);
combined.setModel(nested.getModel());
return ud2groovy(b, combined, blockArgument, nestedExp);
}
}
} else {
// this can only happen with buggy meta-step
LOGGER.log(Level.WARNING, "Buggy meta-step " + d.clazz + " defines no mandatory parameter");
// use the default code path to write it out as: metaStep(describable(...))
}
}
uninst.setSymbol(d.getFunctionName());
return functionCall(b, uninst, blockArgument, nestedExp);
}
}
// unknown type
return b.append("<object of type ").append(clazz.getCanonicalName()).append('>');
}
Aggregations