| // Copyright 2019 The Bazel Authors. All rights reserved. | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| package com.google.devtools.build.lib.syntax; | |
| import com.google.common.collect.ImmutableMap; | |
| import com.google.common.collect.Iterables; | |
| import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; | |
| import com.google.devtools.build.lib.skylarkinterface.SkylarkInterfaceUtils; | |
| import com.google.devtools.build.lib.skylarkinterface.SkylarkModule; | |
| import com.google.devtools.build.lib.util.Pair; | |
| import com.google.errorprone.annotations.CheckReturnValue; | |
| import com.google.errorprone.annotations.FormatMethod; | |
| import java.io.IOException; | |
| import java.io.OutputStream; | |
| import java.time.Duration; | |
| import java.util.List; | |
| import java.util.Map; | |
| import java.util.Set; | |
| import javax.annotation.Nullable; | |
| /** | |
| * The Starlark class defines the most important entry points, constants, and functions needed by | |
| * all clients of the Starlark interpreter. | |
| */ | |
| // TODO(adonovan): move these here: equal, compare, getattr, index, parse, exec, eval, and so on. | |
| public final class Starlark { | |
| private Starlark() {} // uninstantiable | |
| /** The Starlark None value. */ | |
| public static final NoneType NONE = NoneType.NONE; | |
| /** | |
| * A sentinel value passed to optional parameters of SkylarkCallable-annotated methods to indicate | |
| * that no argument value was supplied. | |
| */ | |
| public static final Object UNBOUND = new UnboundMarker(); | |
| @Immutable | |
| private static final class UnboundMarker implements StarlarkValue { | |
| private UnboundMarker() {} | |
| @Override | |
| public String toString() { | |
| return "<unbound>"; | |
| } | |
| @Override | |
| public boolean isImmutable() { | |
| return true; | |
| } | |
| @Override | |
| public void repr(Printer printer) { | |
| printer.append("<unbound>"); | |
| } | |
| } | |
| /** | |
| * The universal bindings predeclared in every Starlark file, such as None, True, len, and range. | |
| */ | |
| public static final ImmutableMap<String, Object> UNIVERSE = makeUniverse(); | |
| private static ImmutableMap<String, Object> makeUniverse() { | |
| ImmutableMap.Builder<String, Object> env = ImmutableMap.builder(); | |
| env // | |
| .put("False", false) | |
| .put("True", true) | |
| .put("None", Starlark.NONE); | |
| addMethods(env, new MethodLibrary()); | |
| return env.build(); | |
| } | |
| /** | |
| * Reports whether the argument is a legal Starlark value: a string, boolean, integer, or | |
| * StarlarkValue. | |
| */ | |
| public static boolean valid(Object x) { | |
| return x instanceof StarlarkValue | |
| || x instanceof String | |
| || x instanceof Boolean | |
| || x instanceof Integer; | |
| } | |
| /** | |
| * Returns {@code x} if it is a {@link #valid} Starlark value, otherwise throws | |
| * IllegalArgumentException. | |
| */ | |
| public static <T> T checkValid(T x) { | |
| if (!valid(x)) { | |
| throw new IllegalArgumentException("invalid Starlark value: " + x.getClass()); | |
| } | |
| return x; | |
| } | |
| /** | |
| * Converts a Java value {@code x} to a Starlark one, if x is not already a valid Starlark value. | |
| * A Java List or Map is converted to a Starlark list or dict, respectively, and null becomes | |
| * {@link #NONE}. Any other non-Starlark value causes the function to throw | |
| * IllegalArgumentException. | |
| * | |
| * <p>This function is applied to the results of @SkylarkCallable-annotated Java methods. | |
| */ | |
| public static Object fromJava(Object x, @Nullable Mutability mutability) { | |
| if (x == null) { | |
| return NONE; | |
| } else if (Starlark.valid(x)) { | |
| return x; | |
| } else if (x instanceof List) { | |
| return StarlarkList.copyOf(mutability, (List<?>) x); | |
| } else if (x instanceof Map) { | |
| return Dict.copyOf(mutability, (Map<?, ?>) x); | |
| } else { | |
| throw new IllegalArgumentException( | |
| "cannot expose internal type to Starlark: " + x.getClass()); | |
| } | |
| } | |
| /** | |
| * Returns the truth value of a valid Starlark value, as if by the Starlark expression {@code | |
| * bool(x)}. | |
| */ | |
| public static boolean truth(Object x) { | |
| if (x instanceof Boolean) { | |
| return (Boolean) x; | |
| } else if (x instanceof StarlarkValue) { | |
| return ((StarlarkValue) x).truth(); | |
| } else if (x instanceof String) { | |
| return !((String) x).isEmpty(); | |
| } else if (x instanceof Integer) { | |
| return (Integer) x != 0; | |
| } else { | |
| throw new IllegalArgumentException("invalid Starlark value: " + x.getClass()); | |
| } | |
| } | |
| /** | |
| * Returns an iterable view of {@code x} if it is an iterable Starlark value; throws EvalException | |
| * otherwise. | |
| * | |
| * <p>Whereas the interpreter temporarily freezes the iterable value using {@link EvalUtils#lock} | |
| * and {@link EvalUtils#unlock} while iterating in {@code for} loops and comprehensions, iteration | |
| * using this method does not freeze the value. Callers should exercise care not to mutate the | |
| * underlying object during iteration. | |
| */ | |
| public static Iterable<?> toIterable(Object x) throws EvalException { | |
| if (x instanceof StarlarkIterable) { | |
| return (Iterable<?>) x; | |
| } | |
| throw errorf("type '%s' is not iterable", type(x)); | |
| } | |
| /** | |
| * Returns a new array containing the elements of Starlark iterable value {@code x}. A Starlark | |
| * value is iterable if it implements {@link StarlarkIterable}. | |
| */ | |
| public static Object[] toArray(Object x) throws EvalException { | |
| // Specialize Sequence and Dict to avoid allocation and/or indirection. | |
| if (x instanceof Sequence) { | |
| return ((Sequence<?>) x).toArray(); | |
| } else if (x instanceof Dict) { | |
| return ((Dict<?, ?>) x).keySet().toArray(); | |
| } else { | |
| return Iterables.toArray(toIterable(x), Object.class); | |
| } | |
| } | |
| /** | |
| * Returns the length of a Starlark string, sequence (such as a list or tuple), dict, or other | |
| * iterable, as if by the Starlark expression {@code len(x)}, or -1 if the value is valid but has | |
| * no length. | |
| */ | |
| public static int len(Object x) { | |
| if (x instanceof String) { | |
| return ((String) x).length(); | |
| } else if (x instanceof Sequence) { | |
| return ((Sequence) x).size(); | |
| } else if (x instanceof Dict) { | |
| return ((Dict) x).size(); | |
| } else if (x instanceof StarlarkIterable) { | |
| // Iterables.size runs in constant time if x implements Collection. | |
| return Iterables.size((Iterable<?>) x); | |
| } else { | |
| checkValid(x); | |
| return -1; // valid but not a sequence | |
| } | |
| } | |
| /** Returns the name of the type of a value as if by the Starlark expression {@code type(x)}. */ | |
| public static String type(Object x) { | |
| return EvalUtils.getDataTypeName(x, false); | |
| } | |
| /** Returns the string form of a value as if by the Starlark expression {@code str(x)}. */ | |
| public static String str(Object x) { | |
| return Printer.getPrinter().str(x).toString(); | |
| } | |
| /** Returns the string form of a value as if by the Starlark expression {@code repr(x)}. */ | |
| public static String repr(Object x) { | |
| return Printer.getPrinter().repr(x).toString(); | |
| } | |
| /** Returns a string formatted as if by the Starlark expression {@code pattern % arguments}. */ | |
| public static String format(String pattern, Object... arguments) { | |
| return Printer.getPrinter().format(pattern, arguments).toString(); | |
| } | |
| /** Returns a string formatted as if by the Starlark expression {@code pattern % arguments}. */ | |
| public static String formatWithList(String pattern, List<?> arguments) { | |
| return Printer.getPrinter().formatWithList(pattern, arguments).toString(); | |
| } | |
| /** Returns a slice of a sequence as if by the Starlark operation {@code x[start:stop:step]}. */ | |
| public static Object slice( | |
| Mutability mu, Object x, Object startObj, Object stopObj, Object stepObj) | |
| throws EvalException { | |
| int n; | |
| if (x instanceof String) { | |
| n = ((String) x).length(); | |
| } else if (x instanceof Sequence) { | |
| n = ((Sequence) x).size(); | |
| } else { | |
| throw errorf("invalid slice operand: %s", type(x)); | |
| } | |
| int start; | |
| int stop; | |
| int step; | |
| // step | |
| if (stepObj == NONE) { | |
| step = 1; | |
| } else { | |
| step = toInt(stepObj, "slice step"); | |
| if (step == 0) { | |
| throw errorf("slice step cannot be zero"); | |
| } | |
| } | |
| // start, stop | |
| if (step > 0) { | |
| // positive stride: default indices are [0:n]. | |
| if (startObj == NONE) { | |
| start = 0; | |
| } else { | |
| start = EvalUtils.toIndex(toInt(startObj, "start index"), n); | |
| } | |
| if (stopObj == NONE) { | |
| stop = n; | |
| } else { | |
| stop = EvalUtils.toIndex(toInt(stopObj, "stop index"), n); | |
| } | |
| if (stop < start) { | |
| stop = start; // => empty result | |
| } | |
| } else { | |
| // negative stride: default indices are effectively [n-1:-1], | |
| // though to get this effect using explicit indices requires | |
| // [n-1:-1-n:-1] because of the treatment of negative values. | |
| if (startObj == NONE) { | |
| start = n - 1; | |
| } else { | |
| start = toInt(startObj, "start index"); | |
| if (start < 0) { | |
| start += n; | |
| } | |
| if (start >= n) { | |
| start = n - 1; | |
| } | |
| } | |
| if (stopObj == NONE) { | |
| stop = -1; | |
| } else { | |
| stop = toInt(stopObj, "stop index"); | |
| if (stop < 0) { | |
| stop += n; | |
| } | |
| if (stop < -1) { | |
| stop = -1; | |
| } | |
| } | |
| if (start < stop) { | |
| start = stop; // => empty result | |
| } | |
| } | |
| // slice operation | |
| if (x instanceof String) { | |
| return StringModule.slice((String) x, start, stop, step); | |
| } else { | |
| return ((Sequence<?>) x).getSlice(mu, start, stop, step); | |
| } | |
| } | |
| static int toInt(Object x, String name) throws EvalException { | |
| if (x instanceof Integer) { | |
| return (Integer) x; | |
| } | |
| throw errorf("got %s for %s, want int", type(x), name); | |
| } | |
| /** | |
| * Calls the function-like value {@code fn} in the specified thread, passing it the given | |
| * positional and named arguments, as if by the Starlark expression {@code fn(*args, **kwargs)}. | |
| */ | |
| public static Object call( | |
| StarlarkThread thread, | |
| Object fn, | |
| List<Object> args, | |
| Map<String, Object> kwargs) | |
| throws EvalException, InterruptedException { | |
| Object[] named = new Object[2 * kwargs.size()]; | |
| int i = 0; | |
| for (Map.Entry<String, Object> e : kwargs.entrySet()) { | |
| named[i++] = e.getKey(); | |
| named[i++] = e.getValue(); | |
| } | |
| return fastcall(thread, fn, args.toArray(), named); | |
| } | |
| /** | |
| * Calls the function-like value {@code fn} in the specified thread, passing it the given | |
| * positional and named arguments in the "fastcall" array representation. | |
| */ | |
| public static Object fastcall( | |
| StarlarkThread thread, Object fn, Object[] positional, Object[] named) | |
| throws EvalException, InterruptedException { | |
| StarlarkCallable callable; | |
| if (fn instanceof StarlarkCallable) { | |
| callable = (StarlarkCallable) fn; | |
| } else { | |
| // @SkylarkCallable(selfCall)? | |
| MethodDescriptor desc = | |
| CallUtils.getSelfCallMethodDescriptor(thread.getSemantics(), fn.getClass()); | |
| if (desc == null) { | |
| throw errorf("'%s' object is not callable", type(fn)); | |
| } | |
| callable = new BuiltinCallable(fn, desc.getName(), desc); | |
| } | |
| thread.push(callable); | |
| try { | |
| return callable.fastcall(thread, positional, named); | |
| } finally { | |
| thread.pop(); | |
| } | |
| } | |
| /** | |
| * Returns a new EvalException with no location and an error message produced by Java-style string | |
| * formatting ({@code String.format(format, args)}). Use {@code errorf("%s", msg)} to produce an | |
| * error message from a non-constant expression {@code msg}. | |
| */ | |
| @FormatMethod | |
| @CheckReturnValue // don't forget to throw it | |
| public static EvalException errorf(String format, Object... args) { | |
| return new EvalException(null, String.format(format, args)); | |
| } | |
| /** Equivalent to {@code addMethods(env, v, DEFAULT_SEMANTICS)}. */ | |
| public static void addMethods(ImmutableMap.Builder<String, Object> env, Object v) { | |
| addMethods(env, v, StarlarkSemantics.DEFAULT_SEMANTICS); | |
| } | |
| /** | |
| * Adds to the environment {@code env} all {@code StarlarkCallable}-annotated fields and methods | |
| * of value {@code v}, filtered by the given semantics. The class of {@code v} must have or | |
| * inherit a {@code SkylarkModule} or {@code SkylarkGlobalLibrary} annotation. | |
| */ | |
| public static void addMethods( | |
| ImmutableMap.Builder<String, Object> env, Object v, StarlarkSemantics semantics) { | |
| Class<?> cls = v.getClass(); | |
| if (!SkylarkInterfaceUtils.hasSkylarkGlobalLibrary(cls) | |
| && SkylarkInterfaceUtils.getSkylarkModule(cls) == null) { | |
| throw new IllegalArgumentException( | |
| cls.getName() + " is annotated with neither @SkylarkGlobalLibrary nor @SkylarkModule"); | |
| } | |
| for (String name : CallUtils.getMethodNames(semantics, v.getClass())) { | |
| // We use the 2-arg (desc=null) BuiltinCallable constructor instead of passing | |
| // the descriptor that CallUtils.getMethod would return, | |
| // because most calls to addMethods pass DEFAULT_SEMANTICS, | |
| // which is probably incorrect for the call. | |
| // The effect is that the default semantics determine which methods appear in | |
| // env, but the thread's semantics determine which method calls succeed. | |
| env.put(name, new BuiltinCallable(v, name)); | |
| } | |
| } | |
| /** | |
| * Adds to the environment {@code env} the value {@code v}, under its annotated name. The class of | |
| * {@code v} must have or inherit a {@code SkylarkModule} annotation. | |
| */ | |
| public static void addModule(ImmutableMap.Builder<String, Object> env, Object v) { | |
| Class<?> cls = v.getClass(); | |
| SkylarkModule annot = SkylarkInterfaceUtils.getSkylarkModule(cls); | |
| if (annot == null) { | |
| throw new IllegalArgumentException(cls.getName() + " is not annotated with @SkylarkModule"); | |
| } | |
| env.put(annot.name(), v); | |
| } | |
| /** | |
| * Checks the {@code positional} and {@code named} arguments supplied to an implementation of | |
| * {@link StarlarkCallable#fastcall} to ensure they match the {@code signature}. It returns an | |
| * array of effective parameter values corresponding to the parameters of the signature. Newly | |
| * allocated values (e.g. a {@code **kwargs} dict) use the Mutability {@code mu}. | |
| * | |
| * <p>If the function has optional parameters, their default values must be supplied by {@code | |
| * defaults}; see {@link BaseFunction#getDefaultValues} for details. | |
| * | |
| * <p>The caller is responsible for accessing the correct element and casting to an appropriate | |
| * type. | |
| * | |
| * <p>On failure, it throws an EvalException incorporating {@code func.toString()}. | |
| */ | |
| public static Object[] matchSignature( | |
| FunctionSignature signature, | |
| StarlarkCallable func, // only used in error messages | |
| @Nullable Tuple<Object> defaults, | |
| @Nullable Mutability mu, | |
| Object[] positional, | |
| Object[] named) | |
| throws EvalException { | |
| // TODO(adonovan): move implementation here. | |
| return BaseFunction.matchSignature(signature, func, defaults, mu, positional, named); | |
| } | |
| // TODO(adonovan): | |
| // | |
| // The code below shows the API that is the destination toward which all of the recent | |
| // tiny steps are headed. It doesn't work yet, but it helps to remember our direction. | |
| // | |
| // The API assumes that the "universe" portion (None, len, str) of the "predeclared" lexical block | |
| // is always available, so clients needn't mention it in the API. Starlark.UNIVERSE will expose it | |
| // as a public constant. | |
| // | |
| // Q. is there any value to returning the Module as opposed to just its global bindings as a Map? | |
| // The Go implementation does the latter and it works well. | |
| // This would allow the the Module class to be private. | |
| // The Bazel "Label" function, and various Bazel caller whitelists, depend on | |
| // being able to dig the label metadata out of a function's module, | |
| // but this could be addressed with a StarlarkFunction.getModuleLabel accessor. | |
| // A. The Module has an associated mutability (that of the thread), | |
| // and it might benefit from a 'freeze' method. | |
| // (But longer term, we might be able to eliminate Thread.mutability, | |
| // and the concept of a shared Mutability entirely, as in go.starlark.net.) | |
| // | |
| // Any FlagRestrictedValues among 'predeclared' and 'env' maps are implicitly filtered by the | |
| // semantics or thread.semantics. | |
| // | |
| // For exec(file), 'predeclared' corresponds exactly to the predeclared environment (sans | |
| // UNIVERSE) as described in the language spec. For eval(expr), 'env' is the complete environment | |
| // in which the expression is evaluated, which might include a mixture of predeclared, global, | |
| // file-local, and function-local variables, as when (for example) the debugger evaluates an | |
| // expression as if at a particular point in the source. As far as 'eval' is concerned, there is | |
| // no difference in kind between these bindings. | |
| // | |
| // The API does not rely on StarlarkThread acting as an environment, or on thread.globals. | |
| // | |
| // These functions could be implemented today with minimal effort. | |
| // The challenge is to migrate all the callers from the old API, | |
| // and in particular to reduce their assumptions about thread.globals, | |
| // which is going away. | |
| // ---- One shot execution API: parse, compile, and execute ---- | |
| /** | |
| * Parse the input as a file, validate it in the specified predeclared environment, compile it, | |
| * and execute it. On success, the module is returned; on failure, it throws an exception. | |
| */ | |
| public static Module exec( | |
| StarlarkThread thread, ParserInput input, Map<String, Object> predeclared) | |
| throws SyntaxError, EvalException, InterruptedException { | |
| // Pseudocode: | |
| // file = StarlarkFile.parse(input) | |
| // validateFile(file, predeclared.keys, thread.semantics) | |
| // prog = compile(file.statements) | |
| // module = new module(predeclared) | |
| // toplevel = new StarlarkFunction(prog.toplevel, module) | |
| // call(thread, toplevel) | |
| // return module # or module.globals? | |
| throw new UnsupportedOperationException(); | |
| } | |
| /** | |
| * Parse the input as an expression, validate it in the specified environment, compile it, and | |
| * evaluate it. On success, the expression's value is returned; on failure, it throws an | |
| * exception. | |
| */ | |
| public static Object eval(StarlarkThread thread, ParserInput input, Map<String, Object> env) | |
| throws SyntaxError, EvalException, InterruptedException { | |
| // Pseudocode: | |
| // StarlarkFunction fn = exprFunc(input, env, thread.semantics) | |
| // return call(thread, fn) | |
| throw new UnsupportedOperationException(); | |
| } | |
| /** | |
| * Parse the input as a file, validate it in the specified environment, compile it, and execute | |
| * it. If the final statement is an expression, return its value. | |
| * | |
| * <p>This complicated function, which combines exec and eval, is intended for use in a REPL or | |
| * debugger. In case of parse of validation error, it throws SyntaxError. In case of execution | |
| * error, the function returns partial results: the incomplete module plus the exception. | |
| * | |
| * <p>Assignments in the input act as updates to a new module created by this function, which is | |
| * returned. | |
| * | |
| * <p>In a typical REPL, the module bindings may be provided as predeclared bindings to the next | |
| * call. | |
| * | |
| * <p>In a typical debugger, predeclared might contain the complete environment at a particular | |
| * point in a running program, including its predeclared, global, and local variables. Assignments | |
| * in the debugger affect only the ephemeral module created by this call, not the values of | |
| * bindings observable by the debugged Starlark program. Thus execAndEval("x = 1; x + x") will | |
| * return a value of 2, and a module containing x=1, but it will not affect the value of any | |
| * variable named x in the debugged program. | |
| * | |
| * <p>A REPL will typically set the legacy "load binds globally" semantics flag, otherwise the | |
| * names bound by a load statement will not be visible in the next REPL chunk. | |
| */ | |
| public static ModuleAndValue execAndEval( | |
| StarlarkThread thread, ParserInput input, Map<String, Object> predeclared) | |
| throws SyntaxError { | |
| // Pseudocode: | |
| // file = StarlarkFile.parse(input) | |
| // validateFile(file, predeclared.keys, thread.semantics) | |
| // prog = compile(file.statements + [return lastexpr]) | |
| // module = new module(predeclared) | |
| // toplevel = new StarlarkFunction(prog.toplevel, module) | |
| // value = call(thread, toplevel) | |
| // return (module, value, error) # or module.globals? | |
| throw new UnsupportedOperationException(); | |
| } | |
| /** | |
| * The triple returned by {@link #execAndEval}. At most one of {@code value} and {@code error} is | |
| * set. | |
| */ | |
| public static class ModuleAndValue { | |
| /** The module, containing global values from top-level assignments. */ | |
| public Module module; | |
| /** The value of the final expression, if any, on success. */ | |
| @Nullable public Object value; | |
| /** An EvalException or InterruptedException, if execution failed. */ | |
| @Nullable public Exception error; | |
| } | |
| // ---- Two-stage API: compilation and execution are separate --- | |
| /** | |
| * Parse the input as a file, validates it in the specified predeclared environment (a set of | |
| * names, optionally filtered by the semantics), and compiles it to a Program. It throws | |
| * SyntaxError in case of scan/parse/validation error. | |
| * | |
| * <p>In addition to the program, it returns the validated syntax tree. This permits clients such | |
| * as Bazel to inspect the syntax (for BUILD dialect checks, glob prefetching, etc.) | |
| */ | |
| public static Pair<Program, StarlarkFile> compileFile( | |
| ParserInput input, // | |
| Set<String> predeclared, | |
| StarlarkSemantics semantics) | |
| throws SyntaxError { | |
| // Pseudocode: | |
| // file = StarlarkFile.parse(input) | |
| // validateFile(file, predeclared.keys, thread.semantics) | |
| // prog = compile(file.statements) | |
| // return (prog, file) | |
| throw new UnsupportedOperationException(); | |
| } | |
| /** | |
| * An opaque executable representation of a StarlarkFile. Programs may be efficiently serialized | |
| * and deserialized without parsing and recompiling. | |
| */ | |
| public static class Program { | |
| /** | |
| * Execute the toplevel function of a compiled program and returns the module populated by its | |
| * top-level assignments. | |
| * | |
| * <p>The keys of predeclared must match the set used when creating the Program. | |
| */ | |
| public Module init( | |
| StarlarkThread thread, // | |
| Map<String, Object> predeclared, | |
| @Nullable Object label) // a regrettable Bazelism we needn't widely expose in the API | |
| throws EvalException, InterruptedException { | |
| // Pseudocode: | |
| // module = new module(predeclared, label=label) | |
| // toplevel = new StarlarkFunction(prog.toplevel, module) | |
| // call(thread, toplevel) | |
| // return module # or module.globals? | |
| throw new UnsupportedOperationException(); | |
| } | |
| } | |
| /** | |
| * Parse the input as an expression, validates it in the specified environment, and returns a | |
| * callable Starlark no-argument function value that computes and returns the value of the | |
| * expression. | |
| */ | |
| private static StarlarkFunction exprFunc( | |
| ParserInput input, // | |
| Map<String, Object> env, | |
| StarlarkSemantics semantics) | |
| throws SyntaxError { | |
| // Pseudocode: | |
| // expr = Expression.parse(input) | |
| // validateExpr(expr, env.keys, semantics) | |
| // prog = compile([return expr]) | |
| // module = new module(env) | |
| // return new StarlarkFunction(prog.toplevel, module) | |
| throw new UnsupportedOperationException(); | |
| } | |
| /** | |
| * Starts the CPU profiler with the specified sampling period, writing a pprof profile to {@code | |
| * out}. All running Starlark threads are profiled. May be called concurrent with Starlark | |
| * execution. | |
| * | |
| * @throws IllegalStateException exception if the Starlark profiler is already running or if the | |
| * operating system's profiling resources for this process are already in use. | |
| */ | |
| public static void startCpuProfile(OutputStream out, Duration period) { | |
| CpuProfiler.start(out, period); | |
| } | |
| /** | |
| * Stops the profiler and waits for the log to be written. Throws an unchecked exception if the | |
| * profiler was not already started by a prior call to {@link #startCpuProfile}. | |
| */ | |
| public static void stopCpuProfile() throws IOException { | |
| CpuProfiler.stop(); | |
| } | |
| } |