/** * ECMA 15.2.3.5 Object.create ( O [, Properties] ) * * @param self self reference * @param proto prototype object * @param props properties to define * @return object created */ @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) public static ScriptObject create(final Object self, final Object proto, final Object props) { if (proto != null) { Global.checkObject(proto); } // FIXME: should we create a proper object with correct number of // properties? final ScriptObject newObj = Global.newEmptyInstance(); newObj.setProto((ScriptObject)proto); if (props != UNDEFINED) { NativeObject.defineProperties(self, newObj, props); } return newObj; }
private Object createObject(final PropertyMap propertyMap, final List<Object> values, final ArrayData arrayData) { final long[] primitiveSpill = dualFields ? new long[values.size()] : null; final Object[] objectSpill = new Object[values.size()]; for (final Property property : propertyMap.getProperties()) { if (!dualFields || property.getType() == Object.class) { objectSpill[property.getSlot()] = values.get(property.getSlot()); } else { primitiveSpill[property.getSlot()] = ObjectClassGenerator.pack((Number) values.get(property.getSlot())); } } final ScriptObject object = dualFields ? new JD(propertyMap, primitiveSpill, objectSpill) : new JO(propertyMap, null, objectSpill); object.setInitialProto(global.getObjectPrototype()); object.setArray(arrayData); return object; }
NativeStrictArguments(final Object[] values, final int numParams,final ScriptObject proto, final PropertyMap map) { super(proto, map); setIsArguments(); final ScriptFunction func = Global.instance().getTypeErrorThrower(); // We have to fill user accessor functions late as these are stored // in this object rather than in the PropertyMap of this object. final int flags = Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE; initUserAccessors("caller", flags, func, func); initUserAccessors("callee", flags, func, func); setArray(ArrayData.allocate(values)); this.length = values.length; // extend/truncate named arg array as needed and copy values this.namedArgs = new Object[numParams]; if (numParams > values.length) { Arrays.fill(namedArgs, UNDEFINED); } System.arraycopy(values, 0, namedArgs, 0, Math.min(namedArgs.length, values.length)); }
@Override public void putAll(final Map<? extends String, ? extends Object> map) { Objects.requireNonNull(map); final ScriptObject oldGlobal = Context.getGlobal(); final boolean globalChanged = (oldGlobal != global); inGlobal(new Callable<Object>() { @Override public Object call() { for (final Map.Entry<? extends String, ? extends Object> entry : map.entrySet()) { final Object value = entry.getValue(); final Object modValue = globalChanged? wrapLikeMe(value, oldGlobal) : value; final String key = entry.getKey(); checkKey(key); sobj.set(key, unwrap(modValue, global), getCallSiteFlags()); } return null; } }); }
/** * ECMA 15.4.4.2 Array.prototype.toString ( ) * * @param self self reference * @return string representation of array */ @Function(attributes = Attribute.NOT_ENUMERABLE) public static Object toString(final Object self) { final Object obj = Global.toObject(self); if (obj instanceof ScriptObject) { final InvokeByName joinInvoker = getJOIN(); final ScriptObject sobj = (ScriptObject)obj; try { final Object join = joinInvoker.getGetter().invokeExact(sobj); if (Bootstrap.isCallable(join)) { return joinInvoker.getInvoker().invokeExact(join, sobj); } } catch (final RuntimeException | Error e) { throw e; } catch (final Throwable t) { throw new RuntimeException(t); } } // FIXME: should lookup Object.prototype.toString and call that? return ScriptRuntime.builtinObjectToString(self); }
@SuppressWarnings("unused") private static Object set__proto__(final Object self, final Object proto) { // See ES6 draft spec: B.2.2.1.2 set Object.prototype.__proto__ // Step 1 Global.checkObjectCoercible(self); // Step 4 if (! (self instanceof ScriptObject)) { return UNDEFINED; } final ScriptObject sobj = (ScriptObject)self; // __proto__ assignment ignores non-nulls and non-objects // step 3: If Type(proto) is neither Object nor Null, then return undefined. if (proto == null || proto instanceof ScriptObject) { sobj.setPrototypeOf(proto); } return UNDEFINED; }
/** * ECMA 15.2.4.6 Object.prototype.isPrototypeOf (V) * * @param self self reference * @param v v prototype object to check against * @return true if object is prototype of v */ @Function(attributes = Attribute.NOT_ENUMERABLE) public static boolean isPrototypeOf(final Object self, final Object v) { if (!(v instanceof ScriptObject)) { return false; } final Object obj = Global.toObject(self); ScriptObject proto = (ScriptObject)v; do { proto = proto.getProto(); if (proto == obj) { return true; } } while (proto != null); return false; }
private List<jdk.nashorn.internal.runtime.Property> extractBuiltinProperties(final String name, final ScriptObject func) { final List<jdk.nashorn.internal.runtime.Property> list = new ArrayList<>(); list.addAll(Arrays.asList(func.getMap().getProperties())); if (func instanceof ScriptFunction) { final ScriptObject proto = ScriptFunction.getPrototype((ScriptFunction)func); if (proto != null) { list.addAll(Arrays.asList(proto.getMap().getProperties())); } } final jdk.nashorn.internal.runtime.Property prop = getProperty(name); if (prop != null) { list.add(prop); } return list; }
/** * Nashorn extension: Object.setPrototypeOf ( O, proto ) * Also found in ES6 draft specification. * * @param self self reference * @param obj object to set prototype for * @param proto prototype object to be used * @return object whose prototype is set */ @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) public static Object setPrototypeOf(final Object self, final Object obj, final Object proto) { if (obj instanceof ScriptObject) { ((ScriptObject)obj).setPrototypeOf(proto); return obj; } else if (obj instanceof ScriptObjectMirror) { ((ScriptObjectMirror)obj).setProto(proto); return obj; } throw notAnObject(obj); }
@Override public void putAll(final Map<? extends String, ? extends Object> map) { final ScriptObject oldGlobal = Context.getGlobal(); final boolean globalChanged = (oldGlobal != global); inGlobal(new Callable<Object>() { @Override public Object call() { for (final Map.Entry<? extends String, ? extends Object> entry : map.entrySet()) { final Object value = entry.getValue(); final Object modValue = globalChanged? wrap(value, oldGlobal) : value; sobj.set(entry.getKey(), unwrap(modValue, global), getCallSiteFlags()); } return null; } }); }
/** * Make a script object mirror on given object if needed. Also converts ConsString instances to Strings. * * @param obj object to be wrapped/converted * @param homeGlobal global to which this object belongs. Not used for ConsStrings. * @return wrapped/converted object */ public static Object wrap(final Object obj, final Object homeGlobal) { if(obj instanceof ScriptObject) { return homeGlobal instanceof Global ? new ScriptObjectMirror((ScriptObject)obj, (Global)homeGlobal) : obj; } if(obj instanceof ConsString) { return obj.toString(); } return obj; }
/** * ECMA 15.4.4.10 Array.prototype.slice ( start [ , end ] ) * * @param self self reference * @param start start of slice (inclusive) * @param end end of slice (optional, exclusive) * @return sliced array */ @Function(attributes = Attribute.NOT_ENUMERABLE) public static Object slice(final Object self, final Object start, final Object end) { final Object obj = Global.toObject(self); if (!(obj instanceof ScriptObject)) { return ScriptRuntime.UNDEFINED; } final ScriptObject sobj = (ScriptObject)obj; final long len = JSType.toUint32(sobj.getLength()); final long relativeStart = JSType.toLong(start); final long relativeEnd = end == ScriptRuntime.UNDEFINED ? len : JSType.toLong(end); long k = relativeStart < 0 ? Math.max(len + relativeStart, 0) : Math.min(relativeStart, len); final long finale = relativeEnd < 0 ? Math.max(len + relativeEnd, 0) : Math.min(relativeEnd, len); if (k >= finale) { return new NativeArray(0); } if (bulkable(sobj)) { return new NativeArray(sobj.getArray().slice(k, finale)); } // Construct array with proper length to have a deleted filter on undefined elements final NativeArray copy = new NativeArray(finale - k); for (long n = 0; k < finale; n++, k++) { if (sobj.has(k)) { copy.defineOwnProperty(ArrayIndex.getArrayIndex(n), sobj.get(k)); } } return copy; }
/** * ArrayLikeIterator factory (reverse order) * @param object object over which to do reverse element iteration * @param includeUndefined should undefined elements be included in the iteration * @return iterator */ public static ArrayLikeIterator<Object> reverseArrayLikeIterator(final Object object, final boolean includeUndefined) { Object obj = object; if (ScriptObject.isArray(obj)) { return new ReverseScriptArrayIterator((ScriptObject) obj, includeUndefined); } obj = JSType.toScriptObject(obj); if (obj instanceof ScriptObject) { return new ReverseScriptObjectIterator((ScriptObject)obj, includeUndefined); } if (obj instanceof JSObject) { return new ReverseJSObjectIterator((JSObject)obj, includeUndefined); } if (obj instanceof List) { return new ReverseJavaListIterator((List<?>)obj, includeUndefined); } if (obj != null && obj.getClass().isArray()) { return new ReverseJavaArrayIterator(obj, includeUndefined); } return new EmptyArrayLikeIterator(); }
/** * ECMA 15.5.4.10 String.prototype.match (regexp) * @param self self reference * @param regexp regexp expression * @return array of regexp matches */ @Function(attributes = Attribute.NOT_ENUMERABLE) public static ScriptObject match(final Object self, final Object regexp) { final String str = checkObjectToString(self); NativeRegExp nativeRegExp; if (regexp == UNDEFINED) { nativeRegExp = new NativeRegExp(""); } else { nativeRegExp = Global.toRegExp(regexp); } if (!nativeRegExp.getGlobal()) { return nativeRegExp.exec(str); } nativeRegExp.setLastIndex(0); int previousLastIndex = 0; final List<Object> matches = new ArrayList<>(); Object result; while ((result = nativeRegExp.exec(str)) != null) { final int thisIndex = nativeRegExp.getLastIndex(); if (thisIndex == previousLastIndex) { nativeRegExp.setLastIndex(thisIndex + 1); previousLastIndex = thisIndex + 1; } else { previousLastIndex = thisIndex; } matches.add(((ScriptObject)result).get(0)); } if (matches.isEmpty()) { return null; } return new NativeArray(matches.toArray()); }
/** * Initialization function for ECMA errors. Stores the error * in the ecmaError field of this class. It is only initialized * once, and then reused * * @param global the global * @return initialized exception */ protected NashornException initEcmaError(final ScriptObject global) { if (ecmaError != null) { return this; // initialized already! } final Object thrown = getThrown(); if (thrown instanceof ScriptObject) { setEcmaError(ScriptObjectMirror.wrap(thrown, global)); } else { setEcmaError(thrown); } return this; }
@SuppressWarnings("unused") private static Object get(final Object self, final Object key) { final CharSequence cs = JSType.toCharSequence(self); final Object primitiveKey = JSType.toPrimitive(key, String.class); final int index = ArrayIndex.getArrayIndex(primitiveKey); if (index >= 0 && index < cs.length()) { return String.valueOf(cs.charAt(index)); } return ((ScriptObject) Global.toObject(self)).get(primitiveKey); }
@SuppressWarnings("unused") private static Object get(final Object self, final double key) { if (isRepresentableAsInt(key)) { return get(self, (int)key); } return ((ScriptObject) Global.toObject(self)).get(key); }
@SuppressWarnings("unused") private static Object get(final Object self, final long key) { final CharSequence cs = JSType.toCharSequence(self); if (key >= 0 && key < cs.length()) { return String.valueOf(cs.charAt((int)key)); } return ((ScriptObject) Global.toObject(self)).get(key); }
/** * ArrayLikeIterator factory * @param object object over which to do reverse element iteration * @param includeUndefined should undefined elements be included in the iteration * @return iterator */ public static ArrayLikeIterator<Object> arrayLikeIterator(final Object object, final boolean includeUndefined) { Object obj = object; if (ScriptObject.isArray(obj)) { return new ScriptArrayIterator((ScriptObject) obj, includeUndefined); } obj = JSType.toScriptObject(obj); if (obj instanceof ScriptObject) { return new ScriptObjectIterator((ScriptObject)obj, includeUndefined); } if (obj instanceof JSObject) { return new JSObjectIterator((JSObject)obj, includeUndefined); } if (obj instanceof List) { return new JavaListIterator((List<?>)obj, includeUndefined); } if (obj != null && obj.getClass().isArray()) { return new JavaArrayIterator(obj, includeUndefined); } return new EmptyArrayLikeIterator(); }
public void test1(final String x, final Object y, final ScriptObject w) { Assert.assertEquals(x, "true"); Assert.assertTrue(y instanceof ScriptObjectMirror); Assert.assertEquals(((ScriptObjectMirror)y).get("foo"), 1); Assert.assertEquals(w.get("bar"), 2); }
/** * ECMA 9.9 ToObject implementation * * @param obj an item for which to run ToObject * @return ToObject version of given item */ public static Object toObject(final Object obj) { if (obj == null || obj == UNDEFINED) { throw typeError("not.an.object", ScriptRuntime.safeToString(obj)); } if (obj instanceof ScriptObject) { return obj; } return instance().wrapAsObject(obj); }
private static StaticClass getAdapterClassFor(final Class<?>[] types, final ScriptObject classOverrides, final ProtectionDomain protectionDomain) { assert types != null && types.length > 0; final SecurityManager sm = System.getSecurityManager(); if (sm != null) { for (final Class<?> type : types) { // check for restricted package access Context.checkPackageAccess(type); // check for classes, interfaces in reflection ReflectionCheckLinker.checkReflectionAccess(type, true); } } return getAdapterInfo(types).getAdapterClass(classOverrides, protectionDomain); }
/** * ECMA 15.4.4.8 Array.prototype.reverse () * * @param self self reference * @return reversed array */ @Function(attributes = Attribute.NOT_ENUMERABLE) public static Object reverse(final Object self) { try { final ScriptObject sobj = (ScriptObject)self; final long len = JSType.toUint32(sobj.getLength()); final long middle = len / 2; for (long lower = 0; lower != middle; lower++) { final long upper = len - lower - 1; final Object lowerValue = sobj.get(lower); final Object upperValue = sobj.get(upper); final boolean lowerExists = sobj.has(lower); final boolean upperExists = sobj.has(upper); if (lowerExists && upperExists) { sobj.set(lower, upperValue, CALLSITE_STRICT); sobj.set(upper, lowerValue, CALLSITE_STRICT); } else if (!lowerExists && upperExists) { sobj.set(lower, upperValue, CALLSITE_STRICT); sobj.delete(upper, true); } else if (lowerExists && !upperExists) { sobj.delete(lower, true); sobj.set(upper, lowerValue, CALLSITE_STRICT); } } return sobj; } catch (final ClassCastException | NullPointerException e) { throw typeError("not.an.object", ScriptRuntime.safeToString(self)); } }
private static InvokeByName getJOIN() { return Global.instance().getInvokeByName(JOIN, new Callable<InvokeByName>() { @Override public InvokeByName call() { return new InvokeByName("join", ScriptObject.class); } }); }
private static MethodHandle getREPLACER_INVOKER() { return Global.instance().getDynamicInvoker(REPLACER_INVOKER, new Callable<MethodHandle>() { @Override public MethodHandle call() { return Bootstrap.createDynamicInvoker("dyn:call", Object.class, ScriptFunction.class, ScriptObject.class, Object.class, Object.class); } }); }
private synchronized ScriptObject getBuiltinJavaApi() { if (getContext().getEnv()._no_java) { throw new IllegalStateException(); } if (this.builtinJavaApi == null) { this.builtinJavaApi = initConstructor("Java", ScriptObject.class); this.builtInJavaExtend = (ScriptFunction)builtinJavaApi.get("extend"); this.builtInJavaTo = (ScriptFunction)builtinJavaApi.get("to"); } return this.builtinJavaApi; }
/** * Make a script object mirror on given object if needed. * * @param obj object to be wrapped * @return wrapped object * @throws IllegalArgumentException if obj cannot be wrapped */ public static ScriptObjectMirror wrap(final Object obj) { if (obj instanceof ScriptObjectMirror) { return (ScriptObjectMirror)obj; } if (obj instanceof ScriptObject) { final ScriptObject sobj = (ScriptObject)obj; return (ScriptObjectMirror) ScriptObjectMirror.wrap(sobj, Context.getGlobal()); } throw new IllegalArgumentException(); }
@SuppressWarnings("unchecked") private static LinkedList<RuntimeEvent<?>> getEventQueue(final Object self) { final ScriptObject sobj = (ScriptObject)self; LinkedList<RuntimeEvent<?>> q; if (sobj.has(EVENT_QUEUE)) { q = (LinkedList<RuntimeEvent<?>>)((ScriptObject)self).get(EVENT_QUEUE); } else { ((ScriptObject)self).set(EVENT_QUEUE, q = new LinkedList<>(), NashornCallSiteDescriptor.CALLSITE_STRICT); } return q; }
private static Long toLong(final Object obj0) { // TODO - Order tests for performance. for (Object obj = obj0; ;) { if (obj == null) { return null; } else if (obj instanceof Long) { return (Long) obj; } else if (obj instanceof Integer) { return ((Integer)obj).longValue(); } else if (obj instanceof Double) { final Double d = (Double)obj; if(Double.isInfinite(d.doubleValue())) { return 0L; } return d.longValue(); } else if (obj instanceof Float) { final Float f = (Float)obj; if(Float.isInfinite(f.floatValue())) { return 0L; } return f.longValue(); } else if (obj instanceof Number) { return ((Number)obj).longValue(); } else if (obj instanceof String || obj instanceof ConsString) { return JSType.toLong(obj); } else if (obj instanceof Boolean) { return (Boolean)obj ? 1L : 0L; } else if (obj instanceof ScriptObject) { obj = JSType.toPrimitive(obj, Number.class); continue; } else if (obj == UNDEFINED) { return null; // null or 0L? } throw assertUnexpectedType(obj); } }
private static ScriptObject splitString(final String str, final String separator, final long limit) { if (separator.isEmpty()) { final int length = (int) Math.min(str.length(), limit); final Object[] array = new Object[length]; for (int i = 0; i < length; i++) { array[i] = String.valueOf(str.charAt(i)); } return new NativeArray(array); } final List<String> elements = new LinkedList<>(); final int strLength = str.length(); final int sepLength = separator.length(); int pos = 0; int n = 0; while (pos < strLength && n < limit) { final int found = str.indexOf(separator, pos); if (found == -1) { break; } elements.add(str.substring(pos, found)); n++; pos = found + sepLength; } if (pos <= strLength && n < limit) { elements.add(str.substring(pos)); } return new NativeArray(elements.toArray()); }
private void initErrorObjects() { // Error objects this.builtinError = initConstructor("Error", ScriptFunction.class); final ScriptObject errorProto = getErrorPrototype(); // Nashorn specific accessors on Error.prototype - stack, lineNumber, columnNumber and fileName final ScriptFunction getStack = ScriptFunction.createBuiltin("getStack", NativeError.GET_STACK); final ScriptFunction setStack = ScriptFunction.createBuiltin("setStack", NativeError.SET_STACK); errorProto.addOwnProperty("stack", Attribute.NOT_ENUMERABLE, getStack, setStack); final ScriptFunction getLineNumber = ScriptFunction.createBuiltin("getLineNumber", NativeError.GET_LINENUMBER); final ScriptFunction setLineNumber = ScriptFunction.createBuiltin("setLineNumber", NativeError.SET_LINENUMBER); errorProto.addOwnProperty("lineNumber", Attribute.NOT_ENUMERABLE, getLineNumber, setLineNumber); final ScriptFunction getColumnNumber = ScriptFunction.createBuiltin("getColumnNumber", NativeError.GET_COLUMNNUMBER); final ScriptFunction setColumnNumber = ScriptFunction.createBuiltin("setColumnNumber", NativeError.SET_COLUMNNUMBER); errorProto.addOwnProperty("columnNumber", Attribute.NOT_ENUMERABLE, getColumnNumber, setColumnNumber); final ScriptFunction getFileName = ScriptFunction.createBuiltin("getFileName", NativeError.GET_FILENAME); final ScriptFunction setFileName = ScriptFunction.createBuiltin("setFileName", NativeError.SET_FILENAME); errorProto.addOwnProperty("fileName", Attribute.NOT_ENUMERABLE, getFileName, setFileName); // ECMA 15.11.4.2 Error.prototype.name // Error.prototype.name = "Error"; errorProto.set(NativeError.NAME, "Error", 0); // ECMA 15.11.4.3 Error.prototype.message // Error.prototype.message = ""; errorProto.set(NativeError.MESSAGE, "", 0); tagBuiltinProperties("Error", builtinError); this.builtinReferenceError = initErrorSubtype("ReferenceError", errorProto); this.builtinSyntaxError = initErrorSubtype("SyntaxError", errorProto); this.builtinTypeError = initErrorSubtype("TypeError", errorProto); }
private ScriptFunction initErrorSubtype(final String name, final ScriptObject errorProto) { final ScriptFunction cons = initConstructor(name, ScriptFunction.class); final ScriptObject prototype = ScriptFunction.getPrototype(cons); prototype.set(NativeError.NAME, name, 0); prototype.set(NativeError.MESSAGE, "", 0); prototype.setInitialProto(errorProto); tagBuiltinProperties(name, cons); return cons; }
private String getStaticSignature() { if (staticSignature == null) { if (paramTypes == null) { staticSignature = Type.getMethodDescriptor(returnType, Type.typeFor(ScriptObject.class), Type.INT); } else { final Type[] params = new Type[paramTypes.length + 2]; params[0] = Type.typeFor(ScriptObject.class); params[1] = Type.INT; System.arraycopy(paramTypes, 0, params, 2, paramTypes.length); staticSignature = Type.getMethodDescriptor(returnType, params); } } return staticSignature; }
/** * ECMA 15.4.4.15 Array.prototype.lastIndexOf ( searchElement [ , fromIndex ] ) * * @param self self reference * @param args arguments: element to search for and optional from index * @return index of element, or -1 if not found */ @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) public static long lastIndexOf(final Object self, final Object... args) { try { final ScriptObject sobj = (ScriptObject)Global.toObject(self); final long len = JSType.toUint32(sobj.getLength()); if (len == 0) { return -1; } final Object searchElement = args.length > 0 ? args[0] : ScriptRuntime.UNDEFINED; final long n = args.length > 1 ? JSType.toLong(args[1]) : len - 1; for (long k = n < 0 ? len - Math.abs(n) : Math.min(n, len - 1); k >= 0; k--) { if (sobj.has(k)) { if (ScriptRuntime.EQ_STRICT(sobj.get(k), searchElement)) { return k; } } } } catch (final ClassCastException | NullPointerException e) { throw typeError("not.an.object", ScriptRuntime.safeToString(self)); } return -1; }
/** * Nashorn extension * Accessed from {@link Global} while setting up the Error.prototype * * @param self self reference * @param value value to set "stack" property to, must be {@code ScriptObject} * * @return value that was set */ public static Object setStack(final Object self, final Object value) { final ScriptObject sobj = Global.checkObject(self); if (sobj.hasOwnProperty(STACK)) { sobj.put(STACK, value, false); } else { sobj.addOwnProperty(STACK, Attribute.NOT_ENUMERABLE, value); } return value; }
private StaticClass getClassAdapterClass(final ScriptObject classOverrides, final ProtectionDomain protectionDomain) { JavaAdapterServices.setClassOverrides(classOverrides); try { return classAdapterGenerator.generateClass(commonLoader, protectionDomain); } finally { JavaAdapterServices.setClassOverrides(null); } }
private synchronized ScriptFunction getBuiltinDate() { if (this.builtinDate == null) { this.builtinDate = initConstructorAndSwitchPoint("Date", ScriptFunction.class); final ScriptObject dateProto = ScriptFunction.getPrototype(builtinDate); // initialize default date this.DEFAULT_DATE = new NativeDate(NaN, dateProto); } return this.builtinDate; }
/** * Inspect class name and decide whether we are generating a ScriptObject class * * @param scriptPrefix the script class prefix for the current script * @param type the type to check * * @return true if type is ScriptObject */ private static boolean isScriptObject(final String scriptPrefix, final String type) { if (type.startsWith(scriptPrefix)) { return true; } else if (type.equals(CompilerConstants.className(ScriptObject.class))) { return true; } else if (type.startsWith(Compiler.OBJECTS_PACKAGE)) { return true; } return false; }
private void tracePrint(final PrintWriter out, final String tag, final Object[] args, final Object result) { //boolean isVoid = type().returnType() == void.class; out.print(Debug.id(this) + " TAG " + tag); out.print(getDescriptor().getName() + "("); if (args.length > 0) { printObject(out, args[0]); for (int i = 1; i < args.length; i++) { final Object arg = args[i]; out.print(", "); if (!(arg instanceof ScriptObject && ((ScriptObject)arg).isScope())) { printObject(out, arg); } else { out.print("SCOPE"); } } } out.print(")"); if (tag.equals("EXIT ")) { out.print(" --> "); printObject(out, result); } out.println(); }