/**
* Copyright (c) 2012-2016 André Bargull
* Alle Rechte vorbehalten / All Rights Reserved. Use is subject to license terms.
*
* <https://github.com/anba/es6draft>
*/
package com.github.anba.es6draft.runtime.types.builtins;
import java.util.Objects;
import com.github.anba.es6draft.runtime.internal.CompatibilityOption;
import com.github.anba.es6draft.runtime.internal.RuntimeInfo;
/**
* Support class to retrieve source code of compiled function objects.
*/
final class FunctionSource {
private static final String NATIVE_CODE = " [native code] ";
private static final String NO_SOURCE = " [no source] ";
private static final int DEFAULT_FLAGS = RuntimeInfo.FunctionFlags.Declaration.getValue();
private FunctionSource() {
}
/**
* Returns the string:
*
* <pre>
* function "functionName"() { [native code] }
* </pre>
*
* @param functionName
* the function name
* @return the function source string
*/
public static String nativeCode(String functionName) {
return sourceString(functionName, NATIVE_CODE, DEFAULT_FLAGS);
}
/**
* Returns the string:
*
* <pre>
* function "functionName"() { [no source] }
* </pre>
*
* @param functionName
* the function name
* @return the function source string
*/
public static String noSource(String functionName) {
return sourceString(functionName, NO_SOURCE, DEFAULT_FLAGS);
}
/**
* Returns the function source string for a user-defined function object.
*
* @param function
* the function object
* @return the function source string
*/
public static String toSourceString(FunctionObject function) {
RuntimeInfo.Function code = function.getCode();
String name = Objects.toString(code.functionName(), "");
int flags = code.functionFlags();
if (RuntimeInfo.FunctionFlags.Native.isSet(flags)) {
return sourceString(name, NATIVE_CODE, flags);
}
RuntimeInfo.FunctionSource source = code.source();
if (source == null) {
return sourceString(name, NO_SOURCE, flags);
}
return sourceString(name, source.parameters(), source.body(), flags,
function.getRealm().isEnabled(CompatibilityOption.ImplicitStrictDirective));
}
private static String sourceString(String name, String body, int flags) {
return sourceString(name, "() ", body, flags, false);
}
private static String sourceString(String name, String parameters, String body, int flags, boolean implicitStrict) {
boolean async = RuntimeInfo.FunctionFlags.Async.isSet(flags);
boolean generator = RuntimeInfo.FunctionFlags.Generator.isSet(flags);
StringBuilder source = new StringBuilder(32 + name.length() + parameters.length() + body.length());
if (RuntimeInfo.FunctionFlags.Arrow.isSet(flags)) {
// ArrowFunction, GeneratorComprehension, AsyncArrowFunction
if (generator) {
// Display generator comprehension as generator function.
source.append("function* ").append(name);
} else if (async) {
source.append("async ");
}
source.append(parameters);
} else if (RuntimeInfo.FunctionFlags.Declaration.isSet(flags)) {
// FunctionDeclaration, (Legacy)GeneratorDeclaration, AsyncFunctionDeclaration
if (async && generator) {
source.append("async function* ");
} else if (async) {
source.append("async function ");
} else if (generator && !RuntimeInfo.FunctionFlags.LegacyGenerator.isSet(flags)) {
source.append("function* ");
} else {
source.append("function ");
}
source.append(name).append(parameters);
} else if (RuntimeInfo.FunctionFlags.Expression.isSet(flags)) {
// FunctionExpression, (Legacy)GeneratorExpression, AsyncFunctionExpression
if (async && generator) {
source.append("async function* ");
} else if (async) {
source.append("async function ");
} else if (generator && !RuntimeInfo.FunctionFlags.LegacyGenerator.isSet(flags)) {
source.append("function* ");
} else {
source.append("function ");
}
if (RuntimeInfo.FunctionFlags.ScopedName.isSet(flags)) {
source.append(name);
}
source.append(parameters);
} else if (RuntimeInfo.FunctionFlags.Method.isSet(flags)) {
// MethodDefinition
if (RuntimeInfo.FunctionFlags.Static.isSet(flags)) {
source.append("static ");
}
if (async && generator) {
source.append("async* ");
} else if (async) {
source.append("async ");
} else if (generator) {
source.append('*');
}
source.append(name).append(parameters);
} else {
// ClassDefinition
assert RuntimeInfo.FunctionFlags.Class.isSet(flags);
// parameters = constructor method, body = optional call constructor method
return source.append(parameters).append(body).toString();
}
if (RuntimeInfo.FunctionFlags.ConciseBody.isSet(flags)) {
source.append(body);
} else {
source.append('{');
if (RuntimeInfo.FunctionFlags.ImplicitStrict.isSet(flags) && implicitStrict) {
source.append("\n\"use strict\";\n");
}
source.append(body).append('}');
}
return source.toString();
}
}