@Override public Object execute( TranslatorDriver.ParserContext parserContext, Object self, MaterializedFrame parentFrame, org.jruby.ast.RootNode rootNode) { try { final RubyParserResult parseResult = truffleContext .getTranslator() .parse( truffleContext, truffleContext.getSourceManager().get(rootNode.getPosition().getFile()), parserContext, parentFrame, rootNode); final CallTarget callTarget = Truffle.getRuntime().createCallTarget(parseResult.getRootNode()); return callTarget.call(RubyArguments.pack(parentFrame, self, null)); } catch (ThrowException e) { throw new RaiseException(truffleContext.getCoreLibrary().nameErrorUncaughtThrow(e.getTag())); } catch (RaiseException | BreakShellException | QuitException e) { throw e; } catch (Throwable e) { e.printStackTrace(); throw new RaiseException(ExceptionTranslator.translateException(truffleContext, e)); } }
@CompilerDirectives.TruffleBoundary public void addMethod(Node currentNode, InternalMethod method) { assert method != null; if (getContext().getCoreLibrary().isLoadingRubyCore()) { final InternalMethod currentMethod = methods.get(method.getName()); if (currentMethod != null && currentMethod.getSharedMethodInfo().getSourceSection() instanceof CoreSourceSection) { return; } } checkFrozen(currentNode); methods.put(method.getName(), method.withDeclaringModule(rubyModuleObject)); newVersion(); if (context.getCoreLibrary().isLoaded() && !method.isUndefined()) { context.send( rubyModuleObject, "method_added", null, context.getSymbolTable().getSymbol(method.getName())); } }
public TraceInstrument(RubyContext context, SourceSection sourceSection) { this.context = context; traceAssumption = context.getTraceManager().getTraceAssumption(); traceFunc = null; callNode = null; event = context.makeString("line"); file = context.makeString(sourceSection.getSource().getName()); line = sourceSection.getStartLine(); }
@CompilerDirectives.SlowPath private RubyString formatSlow(RubyString format, Object[] args) { final RubyContext context = getContext(); if (args.length == 1 && args[0] instanceof RubyArray) { singleArrayProfile.enter(); return context.makeString( StringFormatter.format(format.toString(), ((RubyArray) args[0]).asList())); } else { multipleArgumentsProfile.enter(); return context.makeString(StringFormatter.format(format.toString(), Arrays.asList(args))); } }
public CachedBoxedSymbolDispatchNode( RubyContext context, Object cachedName, DispatchNode next, Object value, InternalMethod method, boolean indirect, DispatchAction dispatchAction) { super(context, cachedName, next, indirect, dispatchAction); unmodifiedAssumption = context.getCoreLibrary().getSymbolClass().getUnmodifiedAssumption(); this.value = value; this.method = method; if (method != null) { if (indirect) { indirectCallNode = Truffle.getRuntime().createIndirectCallNode(); } else { callNode = Truffle.getRuntime().createDirectCallNode(method.getCallTarget()); if (callNode.isCallTargetCloningAllowed() && method.getSharedMethodInfo().shouldAlwaysSplit()) { insert(callNode); callNode.cloneCallTarget(); } } } }
@TruffleBoundary private SafepointAction step(Node currentNode, boolean isDrivingThread) { final DynamicObject thread = context.getThreadManager().getCurrentThread(); // wait other threads to reach their safepoint phaser.arriveAndAwaitAdvance(); if (isDrivingThread) { assumption = Truffle.getRuntime().createAssumption("SafepointManager"); } // wait the assumption to be renewed phaser.arriveAndAwaitAdvance(); // Read these while in the safepoint. SafepointAction deferredAction = deferred ? action : null; try { if (!deferred && thread != null && Layouts.THREAD.getStatus(thread) != Status.ABORTING) { action.run(thread, currentNode); } } finally { // wait other threads to finish their action phaser.arriveAndAwaitAdvance(); } return deferredAction; }
@Override public TruffleMethod truffelize(DynamicMethod originalMethod, ArgsNode argsNode, Node bodyNode) { final MethodDefinitionNode methodDefinitionNode = truffleContext.getTranslator().parse(truffleContext, null, argsNode, bodyNode); return new TruffleMethod( originalMethod, Truffle.getRuntime().createCallTarget(methodDefinitionNode.getMethodRootNode())); }
private Object handleException(VirtualFrame frame, RuntimeException exception) { CompilerAsserts.neverPartOfCompilation(); final RubyContext context = getContext(); final RubyBasicObject rubyException = ExceptionTranslator.translateException(context, exception); context.getCoreLibrary().getGlobalVariablesObject().setInstanceVariable("$!", rubyException); for (RescueNode rescue : rescueParts) { if (rescue.canHandle(frame, rubyException)) { return rescue.execute(frame); } } throw exception; }
public static String expandPath(RubyContext context, String fileName) { // TODO (nirvdrum 11-Feb-15) This needs to work on Windows without calling into non-Truffle // JRuby. if (context.isRunningOnWindows()) { final org.jruby.RubyString path = context.toJRubyString( StringNodes.createString(context.getCoreLibrary().getStringClass(), fileName)); final org.jruby.RubyString expanded = (org.jruby.RubyString) org.jruby.RubyFile.expand_path19( context.getRuntime().getCurrentContext(), null, new org.jruby.runtime.builtin.IRubyObject[] {path}); return expanded.asJavaString(); } else { return expandPath(fileName, null); } }
public TryNode( RubyContext context, SourceSection sourceSection, ExceptionTranslatingNode tryPart, RescueNode[] rescueParts, RubyNode elsePart) { super(context, sourceSection); this.tryPart = tryPart; this.rescueParts = rescueParts; this.elsePart = elsePart; clearExceptionVariableNode = new WriteInstanceVariableNode( context, sourceSection, "$!", new ObjectLiteralNode( context, sourceSection, context.getCoreLibrary().getGlobalVariablesObject()), new ObjectLiteralNode(context, sourceSection, context.getCoreLibrary().getNilObject()), true); }
public CachedBoxedSymbolDispatchNode( RubyContext context, Object cachedName, DispatchNode next, Object value, RubyMethod method) { super(context, cachedName, next); unmodifiedAssumption = context.getCoreLibrary().getSymbolClass().getUnmodifiedAssumption(); this.value = value; this.method = method; if (method != null) { callNode = Truffle.getRuntime().createDirectCallNode(method.getCallTarget()); } }
public synchronized void attach(String file, int line, final DynamicObject block) { assert RubyGuards.isRubyProc(block); final Instrument instrument = Instrument.create( new StandardInstrumentListener() { @Override public void enter(Probe probe, Node node, VirtualFrame frame) { final DynamicObject binding = BindingNodes.createRubyBinding( context.getCoreLibrary().getBindingClass(), RubyArguments.getSelf(frame.getArguments()), frame.materialize()); ProcNodes.rootCall(block, binding); } @Override public void returnVoid(Probe probe, Node node, VirtualFrame virtualFrame) {} @Override public void returnValue( Probe probe, Node node, VirtualFrame virtualFrame, Object o) {} @Override public void returnExceptional( Probe probe, Node node, VirtualFrame virtualFrame, Exception e) {} }, String.format("Truffle::Primitive.attach@%s:%d", file, line)); final Source source = context.getSourceManager().forFileBestFuzzily(file); final LineLocation lineLocation = source.createLineLocation(line); List<Instrument> instruments = attachments.get(lineLocation); if (instruments == null) { instruments = new ArrayList<>(); attachments.put(lineLocation, instruments); } instruments.add(instrument); for (Probe probe : lineToProbesMap.findProbes(lineLocation)) { if (probe.isTaggedAs(StandardSyntaxTag.STATEMENT)) { probe.attach(instrument); return; } } throw new RuntimeException("couldn't find a statement!"); }
public static RubyNode sequence( RubyContext context, SourceSection sourceSection, List<RubyNode> sequence) { final List<RubyNode> flattened = flatten(sequence, true); if (flattened.isEmpty()) { return new ObjectLiteralNode(context, sourceSection, context.getCoreLibrary().getNilObject()); } else if (flattened.size() == 1) { return flattened.get(0); } else { return new SequenceNode( context, sourceSection, flattened.toArray(new RubyNode[flattened.size()])); } }
public synchronized void detach(String file, int line) { final Source source = context.getSourceManager().forFileBestFuzzily(file); final LineLocation lineLocation = source.createLineLocation(line); final List<Instrument> instruments = attachments.remove(lineLocation); if (instruments != null) { for (Instrument instrument : instruments) { instrument.dispose(); } } }
@CompilerDirectives.TruffleBoundary public Object removeClassVariable(Node currentNode, String name) { checkFrozen(currentNode); final Object found = classVariables.remove(name); if (found == null) { CompilerDirectives.transferToInterpreter(); throw new RaiseException( context .getCoreLibrary() .nameErrorClassVariableNotDefined(name, rubyModuleObject, currentNode)); } return found; }
@CompilerDirectives.TruffleBoundary public void changeConstantVisibility(Node currentNode, String name, boolean isPrivate) { checkFrozen(currentNode); RubyConstant rubyConstant = constants.get(name); if (rubyConstant != null) { rubyConstant.setPrivate(isPrivate); newLexicalVersion(); } else { throw new RaiseException( context .getCoreLibrary() .nameErrorUninitializedConstant(rubyModuleObject, name, currentNode)); } }
@Override public void init() { if (RubyContext.PRINT_RUNTIME) { runtime .getInstanceConfig() .getError() .println("jruby: using " + Truffle.getRuntime().getName()); } // Bring in core method nodes CoreMethodNodeManager.addStandardMethods(truffleContext.getCoreLibrary().getObjectClass()); // Give the core library manager a chance to tweak some of those methods truffleContext.getCoreLibrary().initializeAfterMethodsAdded(); // Set program arguments for (IRubyObject arg : ((org.jruby.RubyArray) runtime.getObject().getConstant("ARGV")).toJavaArray()) { assert arg != null; truffleContext.getCoreLibrary().getArgv().slowPush(truffleContext.makeString(arg.toString())); } // Set the load path final RubyArray loadPath = (RubyArray) truffleContext.getCoreLibrary().getGlobalVariablesObject().getInstanceVariable("$:"); for (IRubyObject path : ((org.jruby.RubyArray) runtime.getLoadService().getLoadPath()).toJavaArray()) { loadPath.slowPush(truffleContext.makeString(path.toString())); } // Hook if (truffleContext.getHooks() != null) { truffleContext.getHooks().afterInit(truffleContext); } }
public boolean require(String feature, Node currentNode) throws IOException { final RubyConstant dataConstantBefore = ModuleOperations.lookupConstant( context, LexicalScope.NONE, context.getCoreLibrary().getObjectClass(), "DATA"); if (feature.startsWith("./")) { final String cwd = context.getRuntime().getCurrentDirectory(); feature = cwd + "/" + feature.substring(2); } else if (feature.startsWith("../")) { final String cwd = context.getRuntime().getCurrentDirectory(); feature = cwd.substring(0, cwd.lastIndexOf('/')) + "/" + feature.substring(3); } try { if (isAbsolutePath(feature)) { // Try as a full path if (requireInPath(null, feature, currentNode)) { return true; } } else { // Try each load path in turn for (Object pathObject : ArrayNodes.slowToArray(context.getCoreLibrary().getLoadPath())) { String loadPath = pathObject.toString(); if (!isAbsolutePath(loadPath)) { loadPath = expandPath(context, loadPath); } if (requireInPath(loadPath, feature, currentNode)) { return true; } } } throw new RaiseException(context.getCoreLibrary().loadErrorCannotLoad(feature, currentNode)); } finally { if (dataConstantBefore == null) { context.getCoreLibrary().getObjectClass().removeConstant(currentNode, "DATA"); } else { context .getCoreLibrary() .getObjectClass() .setConstant(currentNode, "DATA", dataConstantBefore.getValue()); } } }
/** Also searches on Object for modules. Used for alias_method, visibility changes, etc. */ @CompilerDirectives.TruffleBoundary public InternalMethod deepMethodSearch(String name) { InternalMethod method = ModuleOperations.lookupMethod(rubyModuleObject, name); if (method != null && !method.isUndefined()) { return method; } // Also search on Object if we are a Module. JRuby calls it deepMethodSearch(). if (RubyGuards.isRubyModule(rubyModuleObject) && !RubyGuards.isRubyClass(rubyModuleObject)) { // TODO: handle undefined methods method = ModuleOperations.lookupMethod(context.getCoreLibrary().getObjectClass(), name); if (method != null && !method.isUndefined()) { return method; } } return null; }
@TruffleBoundary private void assumptionInvalidated(Node currentNode, boolean fromBlockingCall) { final DynamicObject thread = context.getThreadManager().getCurrentThread(); final InterruptMode interruptMode = Layouts.THREAD.getInterruptMode(thread); final boolean interruptible = (interruptMode == InterruptMode.IMMEDIATE) || (fromBlockingCall && interruptMode == InterruptMode.ON_BLOCKING); if (!interruptible) { Thread.currentThread().interrupt(); // keep the interrupt flag return; // interrupt me later } SafepointAction deferredAction = step(currentNode, false); // We're now running again normally and can run deferred actions if (deferredAction != null) { deferredAction.run(thread, currentNode); } }
@TruffleBoundary public void pauseThreadAndExecuteLater( final Thread thread, Node currentNode, final SafepointAction action) { if (Thread.currentThread() == thread) { // fast path if we are already the right thread DynamicObject rubyThread = context.getThreadManager().getCurrentThread(); action.run(rubyThread, currentNode); } else { pauseAllThreadsAndExecute( currentNode, true, new SafepointAction() { @Override public void run(DynamicObject rubyThread, Node currentNode) { if (Thread.currentThread() == thread) { action.run(rubyThread, currentNode); } } }); } }
@Override public void enter(Node node, VirtualFrame frame) { try { traceAssumption.check(); } catch (InvalidAssumptionException e) { traceAssumption = context.getTraceManager().getTraceAssumption(); traceFunc = context.getTraceManager().getTraceFunc(); if (traceFunc != null) { callNode = insert(Truffle.getRuntime().createDirectCallNode(traceFunc.getCallTarget())); } else { callNode = null; } } if (traceFunc != null) { if (!context.getTraceManager().isInTraceFunc()) { context.getTraceManager().setInTraceFunc(true); final Object[] args = new Object[] { event, file, line, NilPlaceholder.INSTANCE, new RubyBinding( context.getCoreLibrary().getBindingClass(), RubyArguments.getSelf(frame.getArguments()), frame.materialize()), NilPlaceholder.INSTANCE }; try { callNode.call( frame, RubyArguments.pack( traceFunc, traceFunc.getDeclarationFrame(), traceFunc.getSelfCapturedInScope(), traceFunc.getBlockCapturedInScope(), args)); } finally { context.getTraceManager().setInTraceFunc(false); } } } }
@Override public void handle(Signal signal) { // TODO: just make this a normal Ruby thread once we don't have the global lock anymore context .getSafepointManager() .pauseAllThreadsAndExecuteFromNonRubyThread( new Consumer<RubyThread>() { @Override public void accept(RubyThread thread) { if (thread == context.getThreadManager().getRootThread()) { context.getThreadManager().enterGlobalLock(thread); try { // assumes this proc does not re-enter the SafepointManager. proc.rootCall(); } finally { context.getThreadManager().leaveGlobalLock(); } } } }); }
@TruffleBoundary public void pauseAllThreadsAndExecute( Node currentNode, boolean deferred, SafepointAction action) { if (lock.isHeldByCurrentThread()) { throw new IllegalStateException("Re-entered SafepointManager"); } // Need to lock interruptibly since we are in the registered threads. while (!lock.tryLock()) { poll(currentNode); } try { pauseAllThreadsAndExecute(currentNode, true, action, deferred); } finally { lock.unlock(); } // Run deferred actions after leaving the SafepointManager lock. if (deferred) { action.run(context.getThreadManager().getCurrentThread(), currentNode); } }
private boolean requireFile(String feature, String path, Node currentNode) throws IOException { // We expect '/' in various classpath URLs, so normalize Windows file paths to use '/' path = path.replace('\\', '/'); final RubyBasicObject loadedFeatures = context.getCoreLibrary().getLoadedFeatures(); if (path.startsWith("uri:classloader:/")) { // TODO CS 13-Feb-15 this uri:classloader:/ and core:/ thing is a hack - simplify it for (Object loaded : Arrays.asList(ArrayNodes.slowToArray(loadedFeatures))) { if (loaded.toString().equals(path)) { return true; } } String coreFileName = path.substring("uri:classloader:/".length()); coreFileName = FileSystems.getDefault().getPath(coreFileName).normalize().toString(); if (context .getRuntime() .getLoadService() .getClassPathResource(context.getRuntime().getJRubyClassLoader(), coreFileName) == null) { return false; } if (SHOW_RESOLUTION) { System.err.printf("resolved %s -> %s%n", feature, coreFileName); } context.getCoreLibrary().loadRubyCore(coreFileName, "uri:classloader:/"); ArrayNodes.slowPush( loadedFeatures, StringNodes.createString(context.getCoreLibrary().getStringClass(), path)); return true; } else if (path.startsWith("core:/")) { for (Object loaded : Arrays.asList(ArrayNodes.slowToArray(loadedFeatures))) { if (loaded.toString().equals(path)) { return true; } } final String coreFileName = path.substring("core:/".length()); if (context .getRuntime() .getLoadService() .getClassPathResource(context.getRuntime().getJRubyClassLoader(), coreFileName) == null) { return false; } if (SHOW_RESOLUTION) { System.err.printf("resolved %s -> %s%n", feature, coreFileName); } context.getCoreLibrary().loadRubyCore(coreFileName, "core:/"); ArrayNodes.slowPush( loadedFeatures, StringNodes.createString(context.getCoreLibrary().getStringClass(), path)); return true; } else { final File file = new File(path); assert file.isAbsolute(); if (!file.isAbsolute() || !file.isFile()) { return false; } final String expandedPath = expandPath(context, path); for (Object loaded : Arrays.asList(ArrayNodes.slowToArray(loadedFeatures))) { if (loaded.toString().equals(expandedPath)) { return true; } } if (SHOW_RESOLUTION) { System.err.printf("resolved %s -> %s%n", feature, expandedPath); } // TODO (nirvdrum 15-Jan-15): If we fail to load, we should remove the path from the loaded // features because subsequent requires of the same statement may succeed. final RubyBasicObject pathString = StringNodes.createString(context.getCoreLibrary().getStringClass(), expandedPath); ArrayNodes.slowPush(loadedFeatures, pathString); try { context.loadFile(path, currentNode); } catch (RaiseException e) { final ArrayMirror mirror = ArrayMirror.reflect((Object[]) ArrayNodes.getStore(loadedFeatures)); final int length = ArrayNodes.getSize(loadedFeatures); for (int i = length - 1; i >= 0; i--) { if (mirror.get(i) == pathString) { for (int j = length - 1; j > i; j--) { mirror.set(i - 1, mirror.get(i)); } ArrayNodes.setSize(loadedFeatures, length - 1); break; } } throw e; } } return true; }
@Override public Object toTruffle(IRubyObject object) { return truffleContext.toTruffle(object); }
@Override public void shutdown() { truffleContext.shutdown(); }
@Override public IRubyObject toJRuby(Object object) { return truffleContext.toJRuby(object); }
public static boolean verifyStore( RubyContext context, Object store, int size, Entry firstInSequence, Entry lastInSequence) { assert store == null || store.getClass() == Object[].class || store instanceof Entry[]; if (store == null) { assert size == 0; assert firstInSequence == null; assert lastInSequence == null; } else if (store instanceof Entry[]) { assert lastInSequence == null || lastInSequence.getNextInSequence() == null; final Entry[] entryStore = (Entry[]) store; Entry foundFirst = null; Entry foundLast = null; int foundSizeBuckets = 0; for (int n = 0; n < entryStore.length; n++) { Entry entry = entryStore[n]; while (entry != null) { foundSizeBuckets++; if (entry == firstInSequence) { assert foundFirst == null; foundFirst = entry; } if (entry == lastInSequence) { assert foundLast == null; foundLast = entry; } entry = entry.getNextInLookup(); } } assert foundSizeBuckets == size; assert firstInSequence == foundFirst; assert lastInSequence == foundLast; int foundSizeSequence = 0; Entry entry = firstInSequence; while (entry != null) { foundSizeSequence++; if (entry.getNextInSequence() == null) { assert entry == lastInSequence; } else { assert entry.getNextInSequence().getPreviousInSequence() == entry; } entry = entry.getNextInSequence(); assert entry != firstInSequence; } assert foundSizeSequence == size : String.format("%d %d", foundSizeSequence, size); } else if (store.getClass() == Object[].class) { assert ((Object[]) store).length == context.getOptions().HASH_PACKED_ARRAY_MAX * PackedArrayStrategy.ELEMENTS_PER_ENTRY : ((Object[]) store).length; final Object[] packedStore = (Object[]) store; for (int n = 0; n < context.getOptions().HASH_PACKED_ARRAY_MAX; n++) { if (n < size) { assert packedStore[n * 2] != null; assert packedStore[n * 2 + 1] != null; } } assert firstInSequence == null; assert lastInSequence == null; } return true; }