package org.raku.rakudo; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Comparator; import org.raku.nqp.runtime.*; import org.raku.nqp.sixmodel.*; import org.raku.nqp.sixmodel.reprs.CallCaptureInstance; import org.raku.nqp.sixmodel.reprs.ContextRefInstance; import org.raku.nqp.sixmodel.reprs.NativeRefInstance; import org.raku.nqp.sixmodel.reprs.VMArrayInstance; /** * Contains implementation of nqp:: ops specific to Rakudo */ @SuppressWarnings("unused") public final class RakOps { public static final boolean DEBUG_MODE = false; public static class ThreadExt { public SixModelObject firstPhaserCodeBlock; public ArrayList prePhaserFrames = new ArrayList(); public ThreadExt(ThreadContext tc) { } } public static class GlobalExt { public SixModelObject Mu; public SixModelObject Any; public SixModelObject Code; public SixModelObject Routine; public SixModelObject Signature; public SixModelObject Parameter; public SixModelObject Int; public SixModelObject Num; public SixModelObject Str; public SixModelObject List; public SixModelObject IterationBuffer; public SixModelObject Iterable; public SixModelObject Array; public SixModelObject Nil; public SixModelObject Map; public SixModelObject Hash; public SixModelObject Junction; public SixModelObject Scalar; public SixModelObject Capture; public SixModelObject ContainerDescriptor; public SixModelObject False; public SixModelObject True; public SixModelObject AutoThreader; public SixModelObject Positional; public SixModelObject PositionalBindFailover; public SixModelObject Associative; public SixModelObject EMPTYARR; public SixModelObject EMPTYHASH; public RakudoJavaInterop rakudoInterop; public SixModelObject JavaHOW; boolean initialized; public GlobalExt(ThreadContext tc) {} } public static ContextKey key = new ContextKey< >(ThreadExt.class, GlobalExt.class); /* Parameter hints for fast lookups. */ private static final int HINT_CODE_DO = 0; private static final int HINT_CODE_SIG = 1; private static final int HINT_ROUTINE_RW = 8; private static final int HINT_SIG_PARAMS = 0; private static final int HINT_SIG_RETURNS = 1; private static final int HINT_SIG_CODE = 4; public static final int HINT_CD_OF = 0; public static final int HINT_CD_NAME = 1; public static final int HINT_CD_DEFAULT = 2; public static SixModelObject p6init(ThreadContext tc) { GlobalExt gcx = key.getGC(tc); if (!gcx.initialized) { tc.gc.contConfigs.put("value_desc_cont", new RakudoContainerConfigurer()); SixModelObject BOOTArray = tc.gc.BOOTArray; gcx.EMPTYARR = BOOTArray.st.REPR.allocate(tc, BOOTArray.st); SixModelObject BOOTHash = tc.gc.BOOTHash; gcx.EMPTYHASH = BOOTHash.st.REPR.allocate(tc, BOOTHash.st); gcx.rakudoInterop = new RakudoJavaInterop(tc.gc); gcx.initialized = true; } return null; } public static SixModelObject p6setitertype(SixModelObject type, ThreadContext tc) { GlobalExt gcx = key.getGC(tc); gcx.Iterable = type; return type; } public static SixModelObject p6setassociativetype(SixModelObject type, ThreadContext tc) { GlobalExt gcx = key.getGC(tc); gcx.Associative = type; return type; } public static SixModelObject p6setiterbuftype(SixModelObject type, ThreadContext tc) { GlobalExt gcx = key.getGC(tc); gcx.IterationBuffer = type; return type; } public static SixModelObject p6settypes(SixModelObject conf, ThreadContext tc) { GlobalExt gcx = key.getGC(tc); gcx.Mu = conf.at_key_boxed(tc, "Mu"); gcx.Any = conf.at_key_boxed(tc, "Any"); gcx.Code = conf.at_key_boxed(tc, "Code"); gcx.Routine = conf.at_key_boxed(tc, "Routine"); gcx.Signature = conf.at_key_boxed(tc, "Signature"); gcx.Parameter = conf.at_key_boxed(tc, "Parameter"); gcx.Int = conf.at_key_boxed(tc, "Int"); gcx.Num = conf.at_key_boxed(tc, "Num"); gcx.Str = conf.at_key_boxed(tc, "Str"); gcx.List = conf.at_key_boxed(tc, "List"); gcx.IterationBuffer = conf.at_key_boxed(tc, "IterationBuffer"); gcx.Iterable = conf.at_key_boxed(tc, "Iterable"); gcx.Array = conf.at_key_boxed(tc, "Array"); gcx.Nil = conf.at_key_boxed(tc, "Nil"); gcx.Map = conf.at_key_boxed(tc, "Map"); gcx.Hash = conf.at_key_boxed(tc, "Hash"); gcx.Junction = conf.at_key_boxed(tc, "Junction"); gcx.Scalar = conf.at_key_boxed(tc, "Scalar"); gcx.Capture = conf.at_key_boxed(tc, "Capture"); gcx.ContainerDescriptor = conf.at_key_boxed(tc, "ContainerDescriptor"); gcx.False = conf.at_key_boxed(tc, "False"); gcx.True = conf.at_key_boxed(tc, "True"); gcx.Associative = conf.at_key_boxed(tc, "Associative"); gcx.JavaHOW = conf.at_key_boxed(tc, "Metamodel").st.WHO.at_key_boxed(tc, "JavaHOW"); return conf; } public static SixModelObject p6setautothreader(SixModelObject autoThreader, ThreadContext tc) { GlobalExt gcx = key.getGC(tc); gcx.AutoThreader = autoThreader; return autoThreader; } public static SixModelObject p6configposbindfailover(SixModelObject p, SixModelObject pbf, ThreadContext tc) { GlobalExt gcx = key.getGC(tc); gcx.Positional = p; gcx.PositionalBindFailover = pbf; return p; } public static SixModelObject booleanize(int x, ThreadContext tc) { GlobalExt gcx = key.getGC(tc); return x == 0 ? gcx.False : gcx.True; } public static SixModelObject p6definite(SixModelObject obj, ThreadContext tc) { GlobalExt gcx = key.getGC(tc); return Ops.isnull(obj) == 1 || Ops.decont(obj, tc) instanceof TypeObject ? gcx.False : gcx.True; } public static SixModelObject p6box_i(long value, ThreadContext tc) { GlobalExt gcx = key.getGC(tc); SixModelObject res = gcx.Int.st.REPR.allocate(tc, gcx.Int.st); res.set_int(tc, value); return res; } public static SixModelObject p6box_u(long value, ThreadContext tc) { GlobalExt gcx = key.getGC(tc); SixModelObject res = gcx.Int.st.REPR.allocate(tc, gcx.Int.st); res.set_int(tc, value); return res; } public static SixModelObject p6box_n(double value, ThreadContext tc) { GlobalExt gcx = key.getGC(tc); SixModelObject res = gcx.Num.st.REPR.allocate(tc, gcx.Num.st); res.set_num(tc, value); return res; } public static SixModelObject p6box_s(String value, ThreadContext tc) { GlobalExt gcx = key.getGC(tc); SixModelObject res = gcx.Str.st.REPR.allocate(tc, gcx.Str.st); res.set_str(tc, value); return res; } public static SixModelObject p6argvmarray(ThreadContext tc, CallSiteDescriptor csd, Object[] args) { SixModelObject BOOTArray = tc.gc.BOOTArray; SixModelObject res = BOOTArray.st.REPR.allocate(tc, BOOTArray.st); for (int i = 0; i < csd.numPositionals; i++) { SixModelObject toBind; switch (csd.argFlags[i]) { case CallSiteDescriptor.ARG_INT: toBind = p6box_i((long)args[i], tc); break; case CallSiteDescriptor.ARG_UINT: toBind = p6box_u((long)args[i], tc); break; case CallSiteDescriptor.ARG_NUM: toBind = p6box_n((double)args[i], tc); break; case CallSiteDescriptor.ARG_STR: toBind = p6box_s((String)args[i], tc); break; default: toBind = Ops.hllize((SixModelObject)args[i], tc); break; } res.bind_pos_boxed(tc, i, toBind); } return res; } public static CallSiteDescriptor p6bindsig(ThreadContext tc, CallSiteDescriptor csd, Object[] args) { /* Do any flattening before processing begins. */ CallFrame cf = tc.curFrame; if (csd.hasFlattening) { csd = csd.explodeFlattening(cf, args); args = tc.flatArgs; } cf.csd = csd; cf.args = args; /* Look up parameters to bind. */ if (DEBUG_MODE) { if (cf.codeRef.name != null) System.err.println("Binding for " + cf.codeRef.name); } GlobalExt gcx = key.getGC(tc); SixModelObject sig = cf.codeRef.codeObject .get_attribute_boxed(tc, gcx.Code, "$!signature", HINT_CODE_SIG); SixModelObject params = sig .get_attribute_boxed(tc, gcx.Signature, "@!params", HINT_SIG_PARAMS); /* Run binder, and handle any errors. */ Object[] error = new Object[3]; switch (Binder.bind(tc, gcx, cf, params, csd, args, false, error)) { case Binder.BIND_RESULT_FAIL: if (error[0] instanceof String) { throw ExceptionHandling.dieInternal(tc, (String) error[0]); } else { Ops.invokeDirect(tc, (SixModelObject) error[0], (CallSiteDescriptor) error[1], (Object[]) error[2]); } case Binder.BIND_RESULT_JUNCTION: /* Invoke the auto-threader. */ csd = csd.injectInvokee(tc, args, cf.codeRef.codeObject); args = tc.flatArgs; Ops.invokeDirect(tc, gcx.AutoThreader, csd, args); Ops.return_o( Ops.result_o(cf), cf); /* Return null to indicate immediate return to the routine. */ return null; } /* The binder may, for a variety of reasons, wind up calling Raku code and overwriting flatArgs, so it needs to be set at the end to return reliably */ tc.flatArgs = args; return csd; } public static SixModelObject p6bindcaptosig(SixModelObject sig, SixModelObject cap, ThreadContext tc) { CallFrame cf = tc.curFrame; GlobalExt gcx = key.getGC(tc); CallSiteDescriptor csd = Binder.explodeCapture(tc, gcx, cap); SixModelObject params = sig.get_attribute_boxed(tc, gcx.Signature, "@!params", HINT_SIG_PARAMS); Object[] error = new Object[3]; switch (Binder.bind(tc, gcx, cf, params, csd, tc.flatArgs, false, error)) { case Binder.BIND_RESULT_FAIL: case Binder.BIND_RESULT_JUNCTION: if (error[0] instanceof String) { throw ExceptionHandling.dieInternal(tc, (String) error[0]); } else { Ops.invokeDirect(tc, (SixModelObject) error[0], (CallSiteDescriptor) error[1], (Object[]) error[2]); } default: return sig; } } public static long p6isbindable(SixModelObject sig, SixModelObject cap, ThreadContext tc) { GlobalExt gcx = key.getGC(tc); CallSiteDescriptor csd; Object[] args; if (cap instanceof CallCaptureInstance) { CallCaptureInstance cc = (CallCaptureInstance)cap; csd = cc.descriptor; args = cc.args; } else { csd = Binder.explodeCapture(tc, gcx, cap); args = tc.flatArgs; } SixModelObject params = sig.get_attribute_boxed(tc, gcx.Signature, "@!params", HINT_SIG_PARAMS); SixModelObject codeObj = sig.get_attribute_boxed(tc, gcx.Signature, "$!code", HINT_SIG_CODE); CodeRef cr = (CodeRef)codeObj.get_attribute_boxed(tc, gcx.Code, "$!do", HINT_CODE_DO); CallFrame cf = new CallFrame(tc, cr); try { switch (Binder.bind(tc, gcx, cf, params, csd, args, false, null)) { case Binder.BIND_RESULT_FAIL: return 0; default: return 1; } } finally { tc.curFrame = tc.curFrame.caller; } } private static final CallSiteDescriptor STORE = new CallSiteDescriptor( new byte[] { CallSiteDescriptor.ARG_OBJ, CallSiteDescriptor.ARG_OBJ }, null); private static final CallSiteDescriptor storeThrower = new CallSiteDescriptor( new byte[] { CallSiteDescriptor.ARG_OBJ }, null); public static SixModelObject p6store(SixModelObject cont, SixModelObject value, ThreadContext tc) { ContainerSpec spec = cont.st.ContainerSpec; if (spec != null) { spec.store(tc, cont, Ops.decont(value, tc)); } else { SixModelObject meth = Ops.findmethodNonFatal(cont, "STORE", tc); if (Ops.isnull(meth) == 0) { Ops.invokeDirect(tc, meth, STORE, new Object[] { cont, value }); } else { SixModelObject thrower = getThrower(tc, "X::Assignment::RO"); if (thrower == null) ExceptionHandling.dieInternal(tc, "Cannot assign to a non-container"); else Ops.invokeDirect(tc, thrower, storeThrower, new Object[] { cont }); } } return cont; } private static final CallSiteDescriptor genIns = new CallSiteDescriptor( new byte[] { CallSiteDescriptor.ARG_OBJ, CallSiteDescriptor.ARG_OBJ, CallSiteDescriptor.ARG_OBJ }, null); private static final CallSiteDescriptor rvThrower = new CallSiteDescriptor( new byte[] { CallSiteDescriptor.ARG_OBJ, CallSiteDescriptor.ARG_OBJ }, null); public static SixModelObject p6typecheckrv(SixModelObject rv, SixModelObject routine, SixModelObject bypassType, ThreadContext tc) { GlobalExt gcx = key.getGC(tc); SixModelObject sig = routine.get_attribute_boxed(tc, gcx.Code, "$!signature", HINT_CODE_SIG); SixModelObject rtype = sig.get_attribute_boxed(tc, gcx.Signature, "$!returns", HINT_SIG_RETURNS); if (rtype != null) { /* The return type could be generic. In that case we have * to call instantiate_generic before doing the type check. */ SixModelObject HOW = rtype.st.HOW; SixModelObject archetypesMeth = Ops.findmethod(HOW, "archetypes", tc); Ops.invokeDirect(tc, archetypesMeth, Ops.invocantCallSite, new Object[] { HOW, rtype }); SixModelObject Archetypes = Ops.result_o(tc.curFrame); SixModelObject genericMeth = Ops.findmethodNonFatal(Archetypes, "generic", tc); if (genericMeth != null) { Ops.invokeDirect(tc, genericMeth, Ops.invocantCallSite, new Object[] { Archetypes }); if (Ops.istrue(Ops.result_o(tc.curFrame), tc) == 1) { SixModelObject ig = Ops.findmethod(HOW, "instantiate_generic", tc); SixModelObject ContextRef = tc.gc.ContextRef; SixModelObject cc = ContextRef.st.REPR.allocate(tc, ContextRef.st); ((ContextRefInstance)cc).context = tc.curFrame; Ops.invokeDirect(tc, ig, genIns, new Object[] { HOW, rtype, cc }); rtype = Ops.result_o(tc.curFrame); } } SixModelObject decontValue = Ops.decont(rv, tc); if (Ops.istype(decontValue, rtype, tc) == 0) { /* Straight type check failed, but it's possible we're returning * an Int that can unbox into an int or similar. */ StorageSpec spec = rtype.st.REPR.get_storage_spec(tc, rtype.st); if (spec.inlineable == 0 || Ops.istype(rtype, decontValue.st.WHAT, tc) == 0) { if (Ops.istype(decontValue.st.WHAT, bypassType, tc) == 0) { SixModelObject thrower = getThrower(tc, "X::TypeCheck::Return"); if (thrower == null) throw ExceptionHandling.dieInternal(tc, "Type check failed for return value"); else Ops.invokeDirect(tc, thrower, rvThrower, new Object[] { decontValue, rtype }); } } } } return rv; } private static final CallSiteDescriptor baThrower = new CallSiteDescriptor( new byte[] { CallSiteDescriptor.ARG_OBJ, CallSiteDescriptor.ARG_OBJ }, null); public static SixModelObject p6bindassert(SixModelObject value, SixModelObject type, ThreadContext tc) { GlobalExt gcx = key.getGC(tc); if (type != gcx.Mu) { SixModelObject decont = Ops.decont(value, tc); if (Ops.istype(decont, type, tc) == 0) { SixModelObject thrower = getThrower(tc, "X::TypeCheck::Binding"); if (thrower == null) ExceptionHandling.dieInternal(tc, "Type check failed in binding"); else Ops.invokeDirect(tc, thrower, baThrower, new Object[] { value, type }); } } return value; } public static SixModelObject p6capturelex(SixModelObject codeObj, ThreadContext tc) { GlobalExt gcx = key.getGC(tc); CodeRef closure = (CodeRef)codeObj.get_attribute_boxed(tc, gcx.Code, "$!do", HINT_CODE_DO); StaticCodeInfo wantedStaticInfo = closure.staticInfo.outerStaticInfo; if (tc.curFrame.codeRef.staticInfo == wantedStaticInfo) closure.outer = tc.curFrame; else if (tc.curFrame.outer.codeRef.staticInfo == wantedStaticInfo) closure.outer = tc.curFrame.outer; return codeObj; } public static SixModelObject p6getouterctx(SixModelObject codeObj, ThreadContext tc) { GlobalExt gcx = key.getGC(tc); codeObj = Ops.decont(codeObj, tc); CodeRef closure = (CodeRef)codeObj.get_attribute_boxed(tc, gcx.Code, "$!do", HINT_CODE_DO); SixModelObject ContextRef = tc.gc.ContextRef; SixModelObject wrap = ContextRef.st.REPR.allocate(tc, ContextRef.st); ((ContextRefInstance)wrap).context = closure.outer; return wrap; } public static SixModelObject p6captureouters2(SixModelObject capList, SixModelObject target, ThreadContext tc) { if (!(target instanceof CodeRef)) ExceptionHandling.dieInternal(tc, "p6captureouters target must be a CodeRef"); CallFrame cf = ((CodeRef)target).outer; if (cf == null) return capList; long elems = capList.elems(tc); for (long i = 0; i < elems; i++) { SixModelObject closure = capList.at_pos_boxed(tc, i); CallFrame ctxToDiddle = ((CodeRef)closure).outer; ctxToDiddle.outer = cf; } return capList; } public static SixModelObject p6bindattrinvres(SixModelObject obj, SixModelObject ch, String name, SixModelObject value, ThreadContext tc) { obj.bind_attribute_boxed(tc, Ops.decont(ch, tc), name, STable.NO_HINT, value); if (obj.sc != null) Ops.scwbObject(tc, obj); return obj; } public static SixModelObject getThrower(ThreadContext tc, String type) { SixModelObject exHash = Ops.gethllsym("Raku", "P6EX", tc); return exHash == null ? null : Ops.atkey(exHash, type, tc); } private static CallFrame find_common_ctx(CallFrame ctx1, CallFrame ctx2) { int depth1 = 0; int depth2 = 0; CallFrame ctx; for (ctx = ctx1; ctx != null; ctx = ctx.caller, depth1++) if (ctx == ctx2) return ctx; for (ctx = ctx2; ctx != null; ctx = ctx.caller, depth2++) if (ctx == ctx1) return ctx; for (; depth1 > depth2; depth2++) ctx1 = ctx1.caller; for (; depth2 > depth1; depth1++) ctx2 = ctx2.caller; while (ctx1 != ctx2) { ctx1 = ctx1.caller; ctx2 = ctx2.caller; } return ctx1; } private static SixModelObject getremotelex(CallFrame pad, String name) { /* use for sub_find_pad */ CallFrame curFrame = pad; while (curFrame != null) { Integer found = curFrame.codeRef.staticInfo.oTryGetLexicalIdx(name); if (found != null) return curFrame.oLex[found]; curFrame = curFrame.outer; } return null; } public static String tclc(String in, ThreadContext tc) { if (in.length() == 0) return in; int first = in.codePointAt(0); return new String(Character.toChars(Character.toTitleCase(first))) + in.substring(Character.charCount(first)).toLowerCase(); } public static long p6stateinit(ThreadContext tc) { return tc.curFrame.stateInit ? 1 : 0; } public static SixModelObject p6setfirstflag(SixModelObject codeObj, ThreadContext tc) { ThreadExt tcx = key.getTC(tc); tcx.firstPhaserCodeBlock = codeObj; return codeObj; } public static long p6takefirstflag(ThreadContext tc) { ThreadExt tcx = key.getTC(tc); boolean matches = tcx.firstPhaserCodeBlock == tc.curFrame.codeRef; tcx.firstPhaserCodeBlock = null; return matches ? 1 : 0; } public static SixModelObject p6setpre(ThreadContext tc) { ThreadExt tcx = key.getTC(tc); tcx.prePhaserFrames.add(tc.curFrame); return null; } public static SixModelObject p6clearpre(ThreadContext tc) { ThreadExt tcx = key.getTC(tc); tcx.prePhaserFrames.remove(tc.curFrame); return null; } public static long p6inpre(ThreadContext tc) { ThreadExt tcx = key.getTC(tc); return tcx.prePhaserFrames.remove(tc.curFrame.caller) ? 1 : 0; } private static final CallSiteDescriptor dispVivifier = new CallSiteDescriptor( new byte[] { CallSiteDescriptor.ARG_OBJ, CallSiteDescriptor.ARG_OBJ, CallSiteDescriptor.ARG_OBJ, CallSiteDescriptor.ARG_OBJ }, null); private static final CallSiteDescriptor dispThrower = new CallSiteDescriptor( new byte[] { CallSiteDescriptor.ARG_STR }, null); public static SixModelObject p6finddispatcher(String usage, ThreadContext tc) { SixModelObject dispatcher = null; CallFrame ctx = tc.curFrame.caller; while (ctx != null) { /* Do we have a dispatcher here? */ StaticCodeInfo sci = ctx.codeRef.staticInfo; Integer dispLexIdx = sci.oTryGetLexicalIdx("$*DISPATCHER"); if (dispLexIdx != null) { SixModelObject maybeDispatcher = ctx.oLex[dispLexIdx]; if (maybeDispatcher != null) { dispatcher = maybeDispatcher; if (dispatcher instanceof TypeObject) { /* Need to vivify it. */ SixModelObject meth = Ops.findmethod(dispatcher, "vivify_for", tc); SixModelObject p6sub = ctx.codeRef.codeObject; SixModelObject ContextRef = tc.gc.ContextRef; SixModelObject wrap = ContextRef.st.REPR.allocate(tc, ContextRef.st); ((ContextRefInstance)wrap).context = ctx; SixModelObject CallCapture = tc.gc.CallCapture; CallCaptureInstance cc = (CallCaptureInstance)CallCapture.st.REPR.allocate(tc, CallCapture.st); cc.descriptor = ctx.csd; cc.args = ctx.args; Ops.invokeDirect(tc, meth, dispVivifier, new Object[] { dispatcher, p6sub, wrap, cc }); dispatcher = Ops.result_o(tc.curFrame); ctx.oLex[dispLexIdx] = dispatcher; } break; } } /* Follow dynamic chain. */ ctx = ctx.caller; } if (dispatcher == null) { SixModelObject thrower = getThrower(tc, "X::NoDispatcher"); if (thrower == null) { ExceptionHandling.dieInternal(tc, usage + " is not in the dynamic scope of a dispatcher"); } else { Ops.invokeDirect(tc, thrower, dispThrower, new Object[] { usage }); } } return dispatcher; } public static SixModelObject p6argsfordispatcher(SixModelObject disp, ThreadContext tc) { SixModelObject result = null; CallFrame ctx = tc.curFrame; while (ctx != null) { /* Do we have the dispatcher we're looking for? */ StaticCodeInfo sci = ctx.codeRef.staticInfo; Integer dispLexIdx = sci.oTryGetLexicalIdx("$*DISPATCHER"); if (dispLexIdx != null) { SixModelObject maybeDispatcher = ctx.oLex[dispLexIdx]; if (maybeDispatcher == disp) { /* Found; grab args. */ SixModelObject CallCapture = tc.gc.CallCapture; CallCaptureInstance cc = (CallCaptureInstance)CallCapture.st.REPR.allocate(tc, CallCapture.st); cc.descriptor = ctx.csd; cc.args = ctx.args; result = cc; break; } } /* Follow dynamic chain. */ ctx = ctx.caller; } if (result == null) throw ExceptionHandling.dieInternal(tc, "Could not find arguments for dispatcher"); return result; } public static SixModelObject p6staticouter(SixModelObject code, ThreadContext tc) { if (code instanceof CodeRef) return ((CodeRef)code).staticInfo.outerStaticInfo.staticCode; else throw ExceptionHandling.dieInternal(tc, "p6staticouter must be used on a CodeRef"); } public static SixModelObject jvmrakudointerop(ThreadContext tc) { GlobalExt gcx = key.getGC(tc); return BootJavaInterop.RuntimeSupport.boxJava(gcx.rakudoInterop, gcx.rakudoInterop.getSTableForClass(RakudoJavaInterop.class)); } }