/** * 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.internal; import static com.github.anba.es6draft.runtime.internal.Errors.newInternalError; import static com.github.anba.es6draft.runtime.types.Undefined.UNDEFINED; import java.io.IOException; import java.nio.file.Path; import java.util.Objects; import com.github.anba.es6draft.Script; import com.github.anba.es6draft.compiler.CompilationException; import com.github.anba.es6draft.compiler.analyzer.CodeSize; import com.github.anba.es6draft.parser.ParserException; import com.github.anba.es6draft.runtime.AbstractOperations; import com.github.anba.es6draft.runtime.ExecutionContext; import com.github.anba.es6draft.runtime.Realm; import com.github.anba.es6draft.runtime.objects.GlobalObject; import com.github.anba.es6draft.runtime.objects.binary.ArrayBuffer; import com.github.anba.es6draft.runtime.objects.binary.ArrayBufferConstructor; import com.github.anba.es6draft.runtime.objects.binary.ArrayBufferObject; import com.github.anba.es6draft.runtime.objects.binary.TypedArrayObject; import com.github.anba.es6draft.runtime.objects.collection.WeakMapObject; import com.github.anba.es6draft.runtime.objects.collection.WeakSetObject; import com.github.anba.es6draft.runtime.objects.iteration.GeneratorObject; import com.github.anba.es6draft.runtime.objects.text.RegExpObject; import com.github.anba.es6draft.runtime.objects.text.RegExpPrototype; import com.github.anba.es6draft.runtime.types.Callable; import com.github.anba.es6draft.runtime.types.Intrinsics; import com.github.anba.es6draft.runtime.types.ScriptObject; import com.github.anba.es6draft.runtime.types.Symbol; import com.github.anba.es6draft.runtime.types.Undefined; import com.github.anba.es6draft.runtime.types.builtins.FunctionObject; import com.github.anba.es6draft.runtime.types.builtins.OrdinaryObject; /** * Default runtime functions. */ public final class RuntimeFunctions { private RuntimeFunctions() { } private static Intrinsics getIntrinsicByName(ExecutionContext cx, String name) { try { return Intrinsics.valueOf(name); } catch (IllegalArgumentException e) { throw newInternalError(cx, Messages.Key.InternalError, "Invalid intrinsic: " + name); } } private static CompatibilityOption getCompatibilityOptionByName(ExecutionContext cx, String name) { try { return CompatibilityOption.valueOf(name); } catch (IllegalArgumentException e) { throw newInternalError(cx, Messages.Key.InternalError, "Invalid compatibility option: " + name); } } /** * Native function: {@code %Include(<file>)}. * <p> * Loads and evaluates the script file. * * @param cx * the execution context * @param file * the file path * @return the script evaluation result */ public static Object Include(ExecutionContext cx, CharSequence file) { Realm realm = cx.getRealm(); Source base = realm.sourceInfo(cx); if (base == null || base.getFile() == null) { throw newInternalError(cx, Messages.Key.InternalError, "No source: " + Objects.toString(base)); } Path path = Objects.requireNonNull(base.getFile().getParent()).resolve(file.toString()); Source source = new Source(path, Objects.requireNonNull(path.getFileName()).toString(), 1); Script script; try { script = realm.getScriptLoader().script(source, path); } catch (ParserException | CompilationException e) { throw e.toScriptException(cx); } catch (IOException e) { throw newInternalError(cx, e, Messages.Key.InternalError, e.toString()); } return script.evaluate(cx); } /** * Native function: {@code %Intrinsic(<name>)}. * <p> * Returns the intrinsic by name. * * @param cx * the execution context * @param name * the intrinsic name * @return the intrinsic */ public static OrdinaryObject Intrinsic(ExecutionContext cx, String name) { Intrinsics id = getIntrinsicByName(cx, name); return cx.getRealm().getIntrinsic(id); } /** * Native function: {@code %SetIntrinsic(<name>, <realm>)}. * <p> * Sets the intrinsic to a new value. * * @param cx * the execution context * @param name * the intrinsic name * @param intrinsic * the new intrinsic object * @return the intrinsic */ public static OrdinaryObject SetIntrinsic(ExecutionContext cx, String name, OrdinaryObject intrinsic) { Intrinsics id = getIntrinsicByName(cx, name); cx.getRealm().setIntrinsic(id, intrinsic); return intrinsic; } /** * Native function: {@code %CallFunction(<function>, <thisValue>, ...<arguments>)}. * <p> * Calls the function object. * * @param cx * the execution context * @param fn * the function * @param thisValue * the this-value * @param args * the function arguments * @return the function return value */ public static Object CallFunction(ExecutionContext cx, Callable fn, Object thisValue, Object... args) { return fn.call(cx, thisValue, args); } /** * Native function: {@code %GlobalTemplate()}. * <p> * Returns the global object template. * * @param cx * the execution context * @return the global object template */ public static GlobalObject GlobalTemplate(ExecutionContext cx) { return cx.getRealm().getGlobalObjectTemplate(); } /** * Native function: {@code %GlobalObject()}. * <p> * Returns the global object. * * @param cx * the execution context * @return the global object */ public static ScriptObject GlobalObject(ExecutionContext cx) { return cx.getRealm().getGlobalObject(); } /** * Native function: {@code %GlobalThis()}. * <p> * Returns the global this. * * @param cx * the execution context * @return the global this */ public static ScriptObject GlobalThis(ExecutionContext cx) { return cx.getRealm().getGlobalThis(); } /** * Native function: {@code %IsArrayBuffer(<value>)}. * <p> * Tests whether the input argument is an ArrayBuffer object. * * @param value * the input argument * @return {@code true} if the object is an ArrayBuffer */ public static boolean IsArrayBuffer(Object value) { return value instanceof ArrayBufferObject; } /** * Native function: {@code %IsCompatibilityOptionEnabled(<name>)}. * <p> * Tests whether or not a compatibility option is enabled * * @param cx * the execution context * @param name * compatibility option name * @return {@code true} if the compatibility option is enabled */ public static boolean IsCompatibilityOptionEnabled(ExecutionContext cx, String name) { return cx.getRealm().isEnabled(getCompatibilityOptionByName(cx, name)); } /** * Native function: {@code %IsDetachedBuffer(<arrayBuffer>)}. * <p> * Tests whether or not the array buffer is detached. * * @param arrayBuffer * the array buffer object * @return {@code true} if the array buffer is detached */ public static boolean IsDetachedBuffer(ArrayBufferObject arrayBuffer) { return ArrayBufferConstructor.IsDetachedBuffer(arrayBuffer); } /** * Native function: {@code %IsFunctionExpression(<function>)}. * <p> * Returns {@code true} if <var>function</var> is a function expression. * * @param function * the function object * @return {@code true} if <var>function</var> is a function expression */ public static boolean IsFunctionExpression(Callable function) { if (!(function instanceof FunctionObject)) { return false; } FunctionObject funObj = (FunctionObject) function; RuntimeInfo.Function code = funObj.getCode(); if (code == null) { return false; } return code.is(RuntimeInfo.FunctionFlags.Expression) && !code.is(RuntimeInfo.FunctionFlags.Arrow); } /** * Native function: {@code %IsGenerator(<value>)}. * <p> * Tests whether the input argument is a generator object. * * @param value * the input argument * @return {@code true} if the object is a generator */ public static boolean IsGenerator(Object value) { return value instanceof GeneratorObject; } /** * Native function: {@code %IsTypedArrayObject(<elementType>, <value>)}. * <p> * Tests whether the input argument is a typed array object. * * @param elementType * the element type name * @param value * the input argument * @return {@code true} if the object is a typed array object */ public static boolean IsTypedArrayObject(CharSequence elementType, Object value) { if (!(value instanceof TypedArrayObject)) { return false; } return ((TypedArrayObject) value).getElementType().getConstructorName().equals(elementType.toString()); } /** * Native function: {@code %RegExpReplace(<regexp>, <string>, <replacement>)}. * <p> * Replaces every occurrence of <var>regexp</var> in <var>string</var> with <var>replacement</var>. * * @param cx * the execution context * @param regexp * the regular expression object * @param string * the input string * @param replacement * the replacement string * @return the result string */ public static String RegExpReplace(ExecutionContext cx, RegExpObject regexp, CharSequence string, CharSequence replacement) { return RegExpPrototype.RegExpReplace(cx, regexp, string.toString(), replacement.toString()); } /** * Native function: {@code %RegExpTest(<regexp>, <string>)}. * <p> * Returns {@code true} if <var>string</var> matches <var>regexp</var>. * * @param cx * the execution context * @param regexp * the regular expression object * @param string * the input string * @return {@code true} if <var>string</var> matches <var>regexp</var> */ public static boolean RegExpTest(ExecutionContext cx, RegExpObject regexp, CharSequence string) { return RegExpPrototype.RegExpTest(cx, regexp, string.toString()); } /** * Native function: {@code %SymbolDescription(<symbol>)}. * <p> * Returns the symbol's description or {@link Undefined#UNDEFINED}. * * @param symbol * the symbol object * @return the symbol's description or {@link Undefined#UNDEFINED} */ public static Object SymbolDescription(Symbol symbol) { return symbol.getDescription() != null ? symbol.getDescription() : UNDEFINED; } /** * Native function: {@code %ToPropertyKey(<value>)}. * <p> * Converts the input argument to a property key. * * @param cx * the execution context * @param value * the input argument * @return the property key */ public static Object ToPropertyKey(ExecutionContext cx, Object value) { return AbstractOperations.ToPropertyKey(cx, value); } /** * Native function: {@code %ToString(<value>)}. * <p> * Converts the input argument to a string. * * @param cx * the execution context * @param value * the input argument * @return the string */ public static Object ToString(ExecutionContext cx, Object value) { return AbstractOperations.ToString(cx, value); } /** * Native function: {@code %WeakMapClear(<weakMap>)}. * <p> * Clears the weak map object. * * @param cx * the execution context * @param weakMap * the weak map object */ public static void WeakMapClear(ExecutionContext cx, Object weakMap) { if (!(weakMap instanceof WeakMapObject)) { throw Errors.newTypeError(cx, Messages.Key.IncompatibleObject); } ((WeakMapObject) weakMap).getWeakMapData().clear(); } /** * Native function: {@code %WeakSetClear(<weakSet>)}. * <p> * Clears the weak set object. * * @param cx * the execution context * @param weakSet * the weak set object */ public static void WeakSetClear(ExecutionContext cx, Object weakSet) { if (!(weakSet instanceof WeakSetObject)) { throw Errors.newTypeError(cx, Messages.Key.IncompatibleObject); } ((WeakSetObject) weakSet).getWeakSetData().clear(); } /** * Creates a new data property. * * @param cx * the execution context * @param object * the script object * @param key * the property key * @param value * the property value */ public static void CreateDataPropertyOrThrow(ExecutionContext cx, ScriptObject object, Object key, Object value) { AbstractOperations.CreateDataPropertyOrThrow(cx, object, ToPropertyKey(cx, key), value); } /** * Returns the array buffer's byte order * * @param cx * the execution context * @param arrayBuffer * the array buffer object * @return the byte order */ public static String ByteOrder(ExecutionContext cx, ArrayBuffer arrayBuffer) { if (arrayBuffer.isDetached()) { return ""; } return arrayBuffer.getData().order().toString(); } /** * Returns the estimated code size for the given source code. * * @param cx * the execution context * @param sourceCode * the source code * @return the estimated code size */ public static int CodeSize(ExecutionContext cx, CharSequence sourceCode) { try { com.github.anba.es6draft.ast.Script parsedScript = cx.getRealm().getScriptLoader() .parseScript(new Source("<script>", 1), sourceCode.toString()); return CodeSize.calculate(parsedScript); } catch (ParserException e) { throw e.toScriptException(cx); } } }