Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Truffle] New system for options. #4335

Merged
merged 17 commits into from Dec 3, 2016
Merged

[Truffle] New system for options. #4335

merged 17 commits into from Dec 3, 2016

Conversation

chrisseaton
Copy link
Contributor

@chrisseaton chrisseaton commented Nov 25, 2016

We need to disconnect from the JRuby system because:

  • We don't want dependencies on the core JRuby code as it causes AOT problems.
  • PolyglotEngine has a configuration system and we should be going through that.
  • Options need to be compilation final.
  • Adding options in multiple places is a chore.
  • Different options should be able to be set for PolyglotEngines in the same VM.

I'm suggesting the system implemented below, with the extra phase that we will code generate some of the classes from the text file. This would be like the parser - we'd commit the generated code as it rarely changes and is small. The code generator would just be a Ruby template - you'd jt build options.

I haven't written the code generator yet. The name NewOptions would just become Options. I didn't make that change to keep this diff small.

All configuration which used to come from RubyInstanceConfig will now be an option. An example of where this is different to before is that arguments (ARGV) would be an option. At the moment there's no way to set this in PolyglotEngine - this new system makes it possible to set it both as a PolyglotEngine configuration option and even in a system property.

I'm proposing we use this one system for all options.

For the difference it makes in the key areas, see

Opinions and votes for and against please @pitr-ch @bjfish @eregon @nirvdrum.

Don't merge.

@chrisseaton
Copy link
Contributor Author

@jtulach is this what we're supposed to be doing with the config in PolyglotEngine? Making that the place where all options can be set?


public class NewOptions {

public final String[] ARGUMENTS;
Copy link
Member

@eregon eregon Nov 25, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally also @CompilationFinal(dimensions = 1)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I didn't know about that - good idea.

@@ -35,6 +36,7 @@ public RubyEngine(RubyInstanceConfig instanceConfig) {

engine = PolyglotEngine.newBuilder()
.config(RubyLanguage.MIME_TYPE, INSTANCE_CONFIG_KEY, instanceConfig)
.config(RubyLanguage.MIME_TYPE, OptionsCatalogue.ARGUMENTS.getName(), instanceConfig.getArgv())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this line needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're getting rid of the RubyInstanceConfig on the line above when I convert all options over. So this is the only way we'll have to communicate arguments across. And it's a good thing because you'll then be able to set Ruby arguments for code that you invoke from other languages.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about passing them as a HashMap or so instead?
This could become quickly a long list, isn't it?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, but options can just not be passed and then use the defaults I guess, except from some key things like ARGV coming from non-system property or OptionDescriptor defaults.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's already a hash map - one that the polyglot engine maintains - these config calls are like put. And it's already private to our language.

There are only ten or so fields like this that we need to pass. We don't need to do this for options that RubyInstanceConfig doesn't currently set.

@eregon
Copy link
Member

eregon commented Nov 25, 2016

Looks good to me 😃 👍

@pitr-ch
Copy link
Member

pitr-ch commented Nov 28, 2016

Looks good 👍 Only one suggestion, could the txt file be something structured? E.g.: a Ruby hash in a file using eval File.read('options.rb'), or a Yaml file (, or JSON)?

@chrisseaton
Copy link
Contributor Author

I thought the simple structure was a bonus? I'll write it as Ruby DSL or a Yaml or JSON file if people want. Chime in if you want that changed.

@pitr-ch
Copy link
Member

pitr-ch commented Nov 28, 2016

But in unstructured txt you have to format manually and then parsed. Hash can be eval-ed (or yaml loaded) and it's done.

@nirvdrum
Copy link
Contributor

I agree with Petr. If the idea is the text file isn't going to change that frequently, I'd rather use an established file format, even if it's more verbose.

@eregon
Copy link
Member

eregon commented Nov 28, 2016

👍 for Yaml.
Alternatively the current format could be easily be made valid CSV, but that's a weird format to manually enter

Copy link
Contributor

@nirvdrum nirvdrum left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the look of this overall. I think we still need an argv processor of some sort though. RubyInstanceConfig takes care of a lot internally that isn't represented here.


// This file would be automatically generated from the list of options in the text file.

public class OptionsCatalogue {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we settled on using the improved American spelling.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@@ -296,7 +296,7 @@

public static final Option<Integer> TRUFFLE_INSTRUMENTATION_SERVER_PORT = integer(TRUFFLE, "truffle.instrumentation_server_port", 0, "Port number to run an HTTP server on that provides instrumentation services");
public static final Option<Boolean> TRUFFLE_EXCEPTIONS_STORE_JAVA = bool(TRUFFLE, "truffle.exceptions.store_java", false, "Store the Java exception with the Ruby backtrace");
public static final Option<Boolean> TRUFFLE_EXCEPTIONS_PRINT_JAVA = bool(TRUFFLE, "truffle.exceptions.print_java", false, "Print Java exceptions at the point of translating them to Ruby exceptions.");
//public static final Option<Boolean> TRUFFLE_EXCEPTIONS_PRINT_JAVA = bool(TRUFFLE, "truffle.exceptions.print_java", false, "Print Java exceptions at the point of translating them to Ruby exceptions.");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I take it this is being removed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes.

@@ -33,7 +33,7 @@
import static org.jruby.util.cli.Options.TRUFFLE_ENCODING_COMPATIBLE_QUERY_CACHE;
import static org.jruby.util.cli.Options.TRUFFLE_ENCODING_LOADED_CLASSES_CACHE;
import static org.jruby.util.cli.Options.TRUFFLE_EVAL_CACHE;
import static org.jruby.util.cli.Options.TRUFFLE_EXCEPTIONS_PRINT_JAVA;
//import static org.jruby.util.cli.Options.TRUFFLE_EXCEPTIONS_PRINT_JAVA;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I take it this is being removed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes.

@@ -159,7 +159,7 @@

public final int INSTRUMENTATION_SERVER_PORT = TRUFFLE_INSTRUMENTATION_SERVER_PORT.load();
public final boolean EXCEPTIONS_STORE_JAVA = TRUFFLE_EXCEPTIONS_STORE_JAVA.load();
public final boolean EXCEPTIONS_PRINT_JAVA = TRUFFLE_EXCEPTIONS_PRINT_JAVA.load();
//public final boolean EXCEPTIONS_PRINT_JAVA = TRUFFLE_EXCEPTIONS_PRINT_JAVA.load();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I take it this is being removed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes.


public void set(Properties properties) {
for (Map.Entry<Object, Object> property : properties.entrySet()) {
final String name = (String) property.getKey();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a case you're trying to handle here by not just calling toString?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Properties are just a very old API and don't use generics.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant is there a reason to avoid property.getKey().toString()?

if (description == null) {
//throw new UnsupportedOperationException(name);

// Don't throw for now - not all the options are transalted across
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be beneficial to print out a warning (configurable via option!) for any option without a description.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will do when they're all in this system.


// Allows input such as [foo, "bar", 'baz']. Doesn't support escape sequences.

private String[] parseStringArray(String string) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unless I've misunderstood the intent of this method, I think it could go away if we just used something like JSON or YAML for the config file format.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess that wouldn't help the argv case, so maybe a value parser is still necessary. It just seems a bit complex.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, what is this for? Do we have a use-case to pass a String[] as a String? A comma-separated list is much nicer to parse and to give on the command line: someOption=foo,bar,baz.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I switched to a simple comma separated format, with escaping for commas if needed. The reason for the string format is so you can set things like Ruby ARGV using system properties. Imagine starting a JS node application and for some reason wanting Ruby code called using interop still have an ARGV.

*/
package org.jruby.truffle.options;

public class OptionTypeException extends UnsupportedOperationException {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it'd be helpful for debugging if this exception type took the option name as its argument. Or perhaps we'd just enforce a message passed with the option name.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

*/
package org.jruby.truffle.options;

public class UnknownOptionException extends UnsupportedOperationException {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As with OptionTypeException, I think it'd help debugging a lot of if the option name were required.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@chrisseaton
Copy link
Contributor Author

Can people review the generator.

I actually have a problem with my ERB templates giving me extra blank lines. Does anyone know how to fix that?

Other than that, raise any final objections now before I do all the work to switch the current options over and merge.


private final Map<OptionDescription, Object> options = new HashMap<>();

public void set(Properties properties) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't readily tell, but I suspect we're going to want a boundary on this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so - you can't set options after the engine is created, so it can't be fast path.


// Allows input such as foo,bar,baz. You can escape commas.

private String[] parseStringArray(String string) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we're going to want a boundary on this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so - you can't set options after the engine is created, so it can't be fast path.

}

}
}).result)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

File.write('truffle/src/main/java/org/jruby/truffle/options/NewOptions.java', ERB.new(<<-JAVA).result)
/*
 * Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved. This
 * code is released under a tri EPL/GPL/LGPL license. You can use it,
 * redistribute it and/or modify it under the terms of the:
 *
 * Eclipse Public License version 1.0
 * GNU General Public License version 2
 * GNU Lesser General Public License version 2.1
 */
package org.jruby.truffle.options;

// This file is automatically generated by options.yml with 'jt build options'

import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;

import javax.annotation.Generated;

@Generated("tool/truffle/generate-options.rb")
public class NewOptions {

    <% options.each do |o| %>
    <% if o.type.end_with?('[]') %>@CompilationFinal(dimensions=1) <% end %>public final <%= o.type %> <%= o.constant %>;
    <% end %>

    NewOptions(OptionsBuilder builder) {
        <% options.each do |o| %>
        <%= o.constant %> = builder.getOrDefault(OptionsCatalog.<%= o.constant %>);
        <% end %>
    }

}
JAVA

gives highlighting in idea

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow! Thanks! There's an example great feature, @eregon.

@pitr-ch
Copy link
Member

pitr-ch commented Dec 1, 2016

[13] pry(main)> ERB.new("<%= :a -%>\n", nil, '-').result
=> "a"

should help

require 'yaml'
require 'erb'

options = YAML.load_file('truffle/src/main/java/org/jruby/truffle/options/options.yml')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this yml file really be in the middle of Java files?
I think it makes more sense outside, like in tool/truffle.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

private final String name;

public OptionTypeException(String name) {
this.name = name;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should super() and given a meaningful message.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@chrisseaton chrisseaton merged commit 9e83c7f into truffle-head Dec 3, 2016
@chrisseaton chrisseaton deleted the truffle-options branch December 3, 2016 10:38
@eregon
Copy link
Member

eregon commented Dec 3, 2016

The full PR looks great, thanks!

@enebo enebo modified the milestone: truffle-dev Jan 10, 2017
@enebo enebo added this to the Non-Release milestone Dec 7, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants