Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Teach JVM about the BOOTSTRAP Binder.
Also use it for its trial_bind implementation and remove
org.perl6.rakudo.Binder.trialBind.
  • Loading branch information
peschwa committed Nov 7, 2015
1 parent 774f0a3 commit ff9e6de
Show file tree
Hide file tree
Showing 4 changed files with 31 additions and 171 deletions.
16 changes: 9 additions & 7 deletions src/Perl6/Metamodel/BOOTSTRAP.nqp
Expand Up @@ -86,8 +86,9 @@ my stub IntPosRef metaclass Perl6::Metamodel::NativeRefHOW { ... };
my stub NumPosRef metaclass Perl6::Metamodel::NativeRefHOW { ... };
my stub StrPosRef metaclass Perl6::Metamodel::NativeRefHOW { ... };

#?if moar
# On MoarVM, implement the signature binder.
# Implement the signature binder.
# The JVM backend really only uses trial_bind,
# so we exclude everything else.
my class Binder {
# Flags that can be set on a signature element.
my int $SIG_ELEM_BIND_CAPTURE := 1;
Expand Down Expand Up @@ -128,6 +129,7 @@ my class Binder {
my $Positional;
my $PositionalBindFailover;

#?if moar
sub arity_fail($params, int $num_params, int $num_pos_args, int $too_many) {
my str $error_prefix := $too_many ?? "Too many" !! "Too few";
my int $count;
Expand Down Expand Up @@ -948,6 +950,11 @@ my class Binder {
$sig
}

method get_return_type($code) {
nqp::getattr(nqp::getattr($code, Code, '$!signature'), Signature, '$!returns')
}
#?endif

my int $TRIAL_BIND_NOT_SURE := 0; # Plausible, but need to check at runtime.
my int $TRIAL_BIND_OK := 1; # Bind will always work out.
my int $TRIAL_BIND_NO_WAY := -1; # Bind could never work out.
Expand Down Expand Up @@ -1070,14 +1077,9 @@ my class Binder {
# Otherwise, if we get there, all is well.
return $TRIAL_BIND_OK;
}

method get_return_type($code) {
nqp::getattr(nqp::getattr($code, Code, '$!signature'), Signature, '$!returns')
}
}
BEGIN { nqp::p6setbinder(Binder); } # We need it in for the next BEGIN block
nqp::p6setbinder(Binder); # The load-time case.
#?endif

# We stick all the declarative bits inside of a BEGIN, so they get
# serialized.
Expand Down
24 changes: 22 additions & 2 deletions src/vm/jvm/Perl6/Ops.nqp
Expand Up @@ -68,9 +68,30 @@ $ops.add_hll_op('perl6', 'p6bindsig', :!inlinable, -> $qastcomp, $op {

$ops.result($il, $RT_VOID);
});
our $Binder;
proto sub trial_bind(*@args) {
$Binder.trial_bind(|@args);
}
my $trial_bind := -> $qastcomp, $op {
$qastcomp.as_jast(QAST::Op.new(
:op('call'),
QAST::WVal.new( :value(nqp::getcodeobj(&trial_bind)) ),
|@($op)
));
};
proto sub set_binder($b) { $Binder := $b; }
proto sub get_binder() { $Binder }
$ops.add_hll_op('nqp', 'p6setbinder', -> $qastcomp, $op {
$qastcomp.as_jast(QAST::Op.new(
:op('call'),
QAST::WVal.new( :value(nqp::getcodeobj(&set_binder)) ),
|@($op)
));
});
$ops.add_hll_op('perl6', 'p6trialbind', :!inlinable, $trial_bind);
$ops.add_hll_op('nqp', 'p6trialbind', :!inlinable, $trial_bind);
$ops.map_classlib_hll_op('perl6', 'p6isbindable', $TYPE_P6OPS, 'p6isbindable', [$RT_OBJ, $RT_OBJ], $RT_INT, :tc);
$ops.map_classlib_hll_op('perl6', 'p6bindcaptosig', $TYPE_P6OPS, 'p6bindcaptosig', [$RT_OBJ, $RT_OBJ], $RT_OBJ, :tc);
$ops.map_classlib_hll_op('perl6', 'p6trialbind', $TYPE_P6OPS, 'p6trialbind', [$RT_OBJ, $RT_OBJ, $RT_OBJ], $RT_INT, :tc);
$ops.map_classlib_hll_op('perl6', 'p6typecheckrv', $TYPE_P6OPS, 'p6typecheckrv', [$RT_OBJ, $RT_OBJ], $RT_OBJ, :tc);
$ops.map_classlib_hll_op('perl6', 'p6decontrv', $TYPE_P6OPS, 'p6decontrv', [$RT_OBJ, $RT_OBJ], $RT_OBJ, :tc);
$ops.map_classlib_hll_op('perl6', 'p6capturelex', $TYPE_P6OPS, 'p6capturelex', [$RT_OBJ], $RT_OBJ, :tc, :!inlinable);
Expand Down Expand Up @@ -198,7 +219,6 @@ $ops.map_classlib_hll_op('nqp', 'p6init', $TYPE_P6OPS, 'p6init', [], $RT_OBJ, :t
$ops.map_classlib_hll_op('nqp', 'p6settypes', $TYPE_P6OPS, 'p6settypes', [$RT_OBJ], $RT_OBJ, :tc);
$ops.map_classlib_hll_op('nqp', 'p6var', $TYPE_P6OPS, 'p6var', [$RT_OBJ], $RT_OBJ, :tc);
$ops.map_classlib_hll_op('nqp', 'p6isbindable', $TYPE_P6OPS, 'p6isbindable', [$RT_OBJ, $RT_OBJ], $RT_INT, :tc);
$ops.map_classlib_hll_op('nqp', 'p6trialbind', $TYPE_P6OPS, 'p6trialbind', [$RT_OBJ, $RT_OBJ, $RT_OBJ], $RT_INT, :tc);
$ops.map_classlib_hll_op('nqp', 'p6inpre', $TYPE_P6OPS, 'p6inpre', [], $RT_INT, :tc);
$ops.map_classlib_hll_op('nqp', 'jvmrakudointerop', $TYPE_P6OPS, 'jvmrakudointerop', [], $RT_OBJ, :tc);
$ops.map_classlib_hll_op('nqp', 'p6captureouters2', $TYPE_P6OPS, 'p6captureouters2', [$RT_OBJ, $RT_OBJ], $RT_OBJ, :tc, :!inlinable);
Expand Down
128 changes: 0 additions & 128 deletions src/vm/jvm/runtime/org/perl6/rakudo/Binder.java
Expand Up @@ -983,132 +983,4 @@ private static SixModelObject vmHashOfRemainingNameds(ThreadContext tc, RakOps.G
}
return slurpy;
}

/* Compile time trial binding; tries to determine at compile time whether
* certain binds will/won't work. */
public static int trialBind(ThreadContext tc, RakOps.GlobalExt gcx, SixModelObject params,
CallSiteDescriptor csd, Object[] args) {
/* If there's a single capture parameter, then we're OK. (Worth
* handling especially as it's the common case for protos). */
int numParams = (int)params.elems(tc);
if (numParams == 1) {
SixModelObject param = params.at_pos_boxed(tc, 0);
param.get_attribute_native(tc, gcx.Parameter, "$!flags", HINT_flags);
int flags = (int)tc.native_i;
if ((flags & SIG_ELEM_IS_CAPTURE) != 0)
return TRIAL_BIND_OK;
}

/* Walk through the signature and consider the parameters. */
int numPosArgs = csd.numPositionals;
int curPosArg = 0;
for (int i = 0; i < numParams; i++) {
/* If the parameter is anything other than a boring old
* positional parameter, we won't analyze it. */
SixModelObject param = params.at_pos_boxed(tc, i);
param.get_attribute_native(tc, gcx.Parameter, "$!flags", HINT_flags);
int flags = (int)tc.native_i;
if ((flags & ~(
SIG_ELEM_MULTI_INVOCANT | SIG_ELEM_IS_RAW |
SIG_ELEM_IS_COPY | SIG_ELEM_ARRAY_SIGIL |
SIG_ELEM_HASH_SIGIL | SIG_ELEM_NATIVE_VALUE |
SIG_ELEM_IS_OPTIONAL)) != 0)
return TRIAL_BIND_NOT_SURE;
SixModelObject namedNames = param.get_attribute_boxed(tc,
gcx.Parameter, "$!named_names", HINT_named_names);
if (namedNames != null)
return TRIAL_BIND_NOT_SURE;
SixModelObject postConstraints = param.get_attribute_boxed(tc,
gcx.Parameter, "$!post_constraints", HINT_post_constraints);
if (postConstraints != null)
return TRIAL_BIND_NOT_SURE;
SixModelObject typeCaptures = param.get_attribute_boxed(tc,
gcx.Parameter, "$!type_captures", HINT_type_captures);
if (typeCaptures != null)
return TRIAL_BIND_NOT_SURE;
SixModelObject coerceType = param.get_attribute_boxed(tc,
gcx.Parameter, "$!coerce_type", HINT_coerce_type);
if (coerceType != null)
return TRIAL_BIND_NOT_SURE;

/* Do we have an argument for this parameter? */
if (curPosArg >= numPosArgs) {
/* No; if it's not optional, fail.*/
if ((flags & SIG_ELEM_IS_OPTIONAL) == 0)
return TRIAL_BIND_NO_WAY;
}
else {
/* Yes, need to consider type. */
int gotPrim = csd.argFlags[curPosArg];
if ((flags & SIG_ELEM_NATIVE_VALUE) != 0) {
if (gotPrim == CallSiteDescriptor.ARG_OBJ) {
/* We got an object; if we aren't sure we can unbox, we can't
* be sure about the dispatch. */
SixModelObject arg = (SixModelObject)args[i];
StorageSpec spec = arg.st.REPR.get_storage_spec(tc, arg.st);
switch (flags & SIG_ELEM_NATIVE_VALUE) {
case SIG_ELEM_NATIVE_INT_VALUE:
if ((spec.can_box & StorageSpec.CAN_BOX_INT) == 0)
return TRIAL_BIND_NOT_SURE;
break;
case SIG_ELEM_NATIVE_NUM_VALUE:
if ((spec.can_box & StorageSpec.CAN_BOX_NUM) == 0)
return TRIAL_BIND_NOT_SURE;
break;
case SIG_ELEM_NATIVE_STR_VALUE:
if ((spec.can_box & StorageSpec.CAN_BOX_STR) == 0)
return TRIAL_BIND_NOT_SURE;
break;
default:
/* WTF... */
return TRIAL_BIND_NOT_SURE;
}
}
else {
/* If it's the wrong type of native, there's no way it
* can ever bind. */
if (((flags & SIG_ELEM_NATIVE_INT_VALUE) != 0 && gotPrim != CallSiteDescriptor.ARG_INT) ||
((flags & SIG_ELEM_NATIVE_NUM_VALUE) != 0 && gotPrim != CallSiteDescriptor.ARG_NUM) ||
((flags & SIG_ELEM_NATIVE_STR_VALUE) != 0 && gotPrim != CallSiteDescriptor.ARG_STR))
return TRIAL_BIND_NO_WAY;
}
}
else {
/* Work out a parameter type to consider, and see if it matches. */
SixModelObject arg =
gotPrim == CallSiteDescriptor.ARG_OBJ ? (SixModelObject)args[curPosArg] :
gotPrim == CallSiteDescriptor.ARG_INT ? gcx.Int :
gotPrim == CallSiteDescriptor.ARG_NUM ? gcx.Num :
gcx.Str;
SixModelObject nominalType = param.get_attribute_boxed(tc,
gcx.Parameter, "$!nominal_type", HINT_nominal_type);
if (nominalType != gcx.Mu && Ops.istype(arg, nominalType, tc) == 0) {
/* If it failed because we got a junction, may auto-thread;
* hand back "not sure" for now. */
if (arg.st.WHAT == gcx.Junction)
return TRIAL_BIND_NOT_SURE;

/* It failed to, but that doesn't mean it can't work at runtime;
* we perhaps want an Int, and the most we know is we have an Any,
* which would include Int. However, the Int ~~ Str case can be
* rejected now, as there's no way it'd ever match. Basically, we
* just flip the type check around. */
return Ops.istype(nominalType, arg, tc) != 0
? TRIAL_BIND_NOT_SURE
: TRIAL_BIND_NO_WAY;
}
}
}

/* Continue to next argument. */
curPosArg++;
}

/* If we have any left over arguments, it's a binding fail. */
if (curPosArg < numPosArgs)
return TRIAL_BIND_NO_WAY;

/* Otherwise, if we get there, all is well. */
return TRIAL_BIND_OK;
}
}
34 changes: 0 additions & 34 deletions src/vm/jvm/runtime/org/perl6/rakudo/RakOps.java
Expand Up @@ -307,40 +307,6 @@ public static long p6isbindable(SixModelObject sig, SixModelObject cap, ThreadCo
}
}

public static long p6trialbind(SixModelObject sig, SixModelObject values, SixModelObject flags, ThreadContext tc) {
/* Get signature and parameters. */
GlobalExt gcx = key.getGC(tc);
SixModelObject params = sig.get_attribute_boxed(tc, gcx.Signature, "$!params", HINT_SIG_PARAMS);

/* Form argument array and call site descriptor. */
int numArgs = (int)values.elems(tc);
Object[] args = new Object[numArgs];
byte[] argFlags = new byte[numArgs];
for (int i = 0; i < numArgs; i++) {
switch ((int)flags.at_pos_boxed(tc, i).get_int(tc)) {
case CallSiteDescriptor.ARG_INT:
args[i] = 0;
argFlags[i] = CallSiteDescriptor.ARG_INT;
break;
case CallSiteDescriptor.ARG_NUM:
args[i] = 0.0;
argFlags[i] = CallSiteDescriptor.ARG_NUM;
break;
case CallSiteDescriptor.ARG_STR:
args[i] = "";
argFlags[i] = CallSiteDescriptor.ARG_STR;
break;
default:
args[i] = values.at_pos_boxed(tc, i);
argFlags[i] = CallSiteDescriptor.ARG_OBJ;
break;
}
}

/* Do trial bind. */
return Binder.trialBind(tc, gcx, params, new CallSiteDescriptor(argFlags, null), args);
}

private static final CallSiteDescriptor STORE = new CallSiteDescriptor(
new byte[] { CallSiteDescriptor.ARG_OBJ, CallSiteDescriptor.ARG_OBJ }, null);
private static final CallSiteDescriptor storeThrower = new CallSiteDescriptor(
Expand Down

0 comments on commit ff9e6de

Please sign in to comment.