Skip to content

Commit 47b99a3

Browse files
eregonandrykonchin
authored andcommitted
[GR-17457] We no longer need to look into a Refinement's ancestors for method lookup
PullRequest: truffleruby/4257
2 parents 98ba377 + dc9ac5a commit 47b99a3

File tree

2 files changed

+65
-138
lines changed

2 files changed

+65
-138
lines changed

src/main/java/org/truffleruby/core/module/ModuleOperations.java

Lines changed: 55 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -150,8 +150,7 @@ public static ConstantLookupResult lookupConstant(RubyContext context, RubyModul
150150
private static ConstantLookupResult lookupConstant(RubyContext context, RubyModule module, String name,
151151
ArrayList<Assumption> assumptions) {
152152
// Look in the current module
153-
ModuleFields fields = module.fields;
154-
ConstantEntry constantEntry = fields.getOrComputeConstantEntry(name);
153+
ConstantEntry constantEntry = module.fields.getOrComputeConstantEntry(name);
155154
assumptions.add(constantEntry.getAssumption());
156155
if (constantExists(constantEntry, assumptions)) {
157156
return new ConstantLookupResult(constantEntry.getConstant(), toArray(assumptions));
@@ -162,8 +161,7 @@ private static ConstantLookupResult lookupConstant(RubyContext context, RubyModu
162161
if (ancestor == module) {
163162
continue;
164163
}
165-
fields = ancestor.fields;
166-
constantEntry = fields.getOrComputeConstantEntry(name);
164+
constantEntry = ancestor.fields.getOrComputeConstantEntry(name);
167165
assumptions.add(constantEntry.getAssumption());
168166
if (constantExists(constantEntry, assumptions)) {
169167
return new ConstantLookupResult(constantEntry.getConstant(), toArray(assumptions));
@@ -179,16 +177,14 @@ public static ConstantLookupResult lookupConstantInObject(RubyContext context, S
179177
ArrayList<Assumption> assumptions) {
180178
final RubyClass objectClass = context.getCoreLibrary().objectClass;
181179

182-
ModuleFields fields = objectClass.fields;
183-
ConstantEntry constantEntry = fields.getOrComputeConstantEntry(name);
180+
ConstantEntry constantEntry = objectClass.fields.getOrComputeConstantEntry(name);
184181
assumptions.add(constantEntry.getAssumption());
185182
if (constantExists(constantEntry, assumptions)) {
186183
return new ConstantLookupResult(constantEntry.getConstant(), toArray(assumptions));
187184
}
188185

189186
for (RubyModule ancestor : objectClass.fields.prependedAndIncludedModules()) {
190-
fields = ancestor.fields;
191-
constantEntry = fields.getOrComputeConstantEntry(name);
187+
constantEntry = ancestor.fields.getOrComputeConstantEntry(name);
192188
assumptions.add(constantEntry.getAssumption());
193189
if (constantExists(constantEntry, assumptions)) {
194190
return new ConstantLookupResult(constantEntry.getConstant(), toArray(assumptions));
@@ -202,15 +198,13 @@ public static ConstantLookupResult lookupConstantInObject(RubyContext context, S
202198
public static RubyConstant lookupConstantInObjectUncached(RubyContext context, String name) {
203199
final RubyClass objectClass = context.getCoreLibrary().objectClass;
204200

205-
ModuleFields fields = objectClass.fields;
206-
RubyConstant constant = fields.getConstant(name);
201+
RubyConstant constant = objectClass.fields.getConstant(name);
207202
if (constantExists(constant, null)) {
208203
return constant;
209204
}
210205

211206
for (RubyModule ancestor : objectClass.fields.prependedAndIncludedModules()) {
212-
fields = ancestor.fields;
213-
constant = fields.getConstant(name);
207+
constant = ancestor.fields.getConstant(name);
214208
if (constantExists(constant, null)) {
215209
return constant;
216210
}
@@ -243,8 +237,7 @@ public static ConstantLookupResult lookupConstantWithLexicalScope(RubyContext co
243237

244238
// Look in lexical scope
245239
while (lexicalScope != context.getRootLexicalScope()) {
246-
final ModuleFields fields = lexicalScope.getLiveModule().fields;
247-
final ConstantEntry constantEntry = fields.getOrComputeConstantEntry(name);
240+
final ConstantEntry constantEntry = lexicalScope.getLiveModule().fields.getOrComputeConstantEntry(name);
248241
assumptions.add(constantEntry.getAssumption());
249242
if (constantExists(constantEntry, assumptions)) {
250243
return new ConstantLookupResult(constantEntry.getConstant(), toArray(assumptions));
@@ -328,8 +321,7 @@ public static ConstantLookupResult lookupConstantWithInherit(RubyContext context
328321
return ModuleOperations.lookupConstant(context, module, name, assumptions);
329322
}
330323
} else {
331-
final ModuleFields fields = module.fields;
332-
final ConstantEntry constantEntry = fields.getOrComputeConstantEntry(name);
324+
final ConstantEntry constantEntry = module.fields.getOrComputeConstantEntry(name);
333325
assumptions.add(constantEntry.getAssumption());
334326
if (constantExists(constantEntry, assumptions)) {
335327
return new ConstantLookupResult(constantEntry.getConstant(), toArray(assumptions));
@@ -426,49 +418,25 @@ public static Map<String, InternalMethod> withoutUndefinedMethods(Map<String, In
426418
return definedMethods;
427419
}
428420

429-
public static MethodLookupResult lookupMethodCached(RubyModule module, String name,
430-
DeclarationContext declarationContext) {
431-
return lookupMethodCached(module, null, name, declarationContext);
432-
}
433-
434421
@TruffleBoundary
435-
private static MethodLookupResult lookupMethodCached(RubyModule module, RubyModule lookupTo, String name,
422+
public static MethodLookupResult lookupMethodCached(RubyModule module, String name,
436423
DeclarationContext declarationContext) {
437-
final ArrayList<Assumption> assumptions = new ArrayList<>();
424+
var assumptions = new ArrayList<Assumption>();
438425

439426
// Look in ancestors
440427
for (RubyModule ancestor : module.fields.ancestors()) {
441-
if (ancestor == lookupTo) {
442-
return new MethodLookupResult(null, toArray(assumptions));
443-
}
444-
final RubyModule[] refinements = getRefinementsFor(declarationContext, ancestor);
445-
428+
var refinements = getRefinementsFor(declarationContext, ancestor);
446429
if (refinements != null) {
447430
for (RubyModule refinement : refinements) {
448-
// If we have more then one active refinement for C (where C is refined module):
449-
// R1.ancestors = [R1, A, C, ...]
450-
// R2.ancestors = [R2, B, C, ...]
451-
// R3.ancestors = [R3, D, C, ...]
452-
// we are only looking up to C
453-
// R3 -> D -> R2 -> B -> R1 -> A
454-
final MethodLookupResult refinedMethod = lookupMethodCached(
455-
refinement,
456-
ancestor,
457-
name,
458-
null);
459-
for (Assumption assumption : refinedMethod.getAssumptions()) {
460-
assumptions.add(assumption);
461-
}
462-
if (refinedMethod.isDefined()) {
463-
InternalMethod method = rememberUsedRefinements(refinedMethod.getMethod(), declarationContext);
431+
var refinedMethod = refinement.fields.getMethodAndAssumption(name, assumptions);
432+
if (refinedMethod != null) {
433+
InternalMethod method = rememberUsedRefinements(refinedMethod, declarationContext);
464434
return new MethodLookupResult(method, toArray(assumptions));
465435
}
466436
}
467437
}
468438

469-
final ModuleFields fields = ancestor.fields;
470-
final InternalMethod method = fields.getMethodAndAssumption(name, assumptions);
471-
439+
var method = ancestor.fields.getMethodAndAssumption(name, assumptions);
472440
if (method != null) {
473441
return new MethodLookupResult(method, toArray(assumptions));
474442
}
@@ -478,37 +446,22 @@ private static MethodLookupResult lookupMethodCached(RubyModule module, RubyModu
478446
return new MethodLookupResult(null, toArray(assumptions));
479447
}
480448

481-
public static InternalMethod lookupMethodUncached(RubyModule module, String name,
482-
DeclarationContext declarationContext) {
483-
return lookupMethodUncached(module, null, name, declarationContext);
484-
}
485-
486449
@TruffleBoundary
487-
private static InternalMethod lookupMethodUncached(RubyModule module, RubyModule lookupTo, String name,
450+
public static InternalMethod lookupMethodUncached(RubyModule module, String name,
488451
DeclarationContext declarationContext) {
489-
452+
// Look in ancestors
490453
for (RubyModule ancestor : module.fields.ancestors()) {
491-
if (ancestor == lookupTo) {
492-
return null;
493-
}
494-
final RubyModule[] refinements = getRefinementsFor(declarationContext, ancestor);
495-
454+
var refinements = getRefinementsFor(declarationContext, ancestor);
496455
if (refinements != null) {
497456
for (RubyModule refinement : refinements) {
498-
final InternalMethod refinedMethod = lookupMethodUncached(
499-
refinement,
500-
ancestor,
501-
name,
502-
null);
457+
var refinedMethod = refinement.fields.getMethod(name);
503458
if (refinedMethod != null) {
504459
return rememberUsedRefinements(refinedMethod, declarationContext);
505460
}
506461
}
507462
}
508463

509-
final ModuleFields fields = ancestor.fields;
510-
final InternalMethod method = fields.getMethod(name);
511-
464+
var method = ancestor.fields.getMethod(name);
512465
if (method != null) {
513466
return method;
514467
}
@@ -518,82 +471,62 @@ private static InternalMethod lookupMethodUncached(RubyModule module, RubyModule
518471
return null;
519472
}
520473

474+
@TruffleBoundary
521475
public static MethodLookupResult lookupSuperMethod(InternalMethod currentMethod, RubyModule objectMetaClass) {
522-
final String name = currentMethod.getSharedMethodInfo().getMethodNameForNotBlock(); // use the original name
476+
var name = currentMethod.getSharedMethodInfo().getMethodNameForNotBlock(); // use the original name
523477

524-
Memo<Boolean> foundDeclaringModule = new Memo<>(false);
525-
return lookupSuperMethod(
526-
currentMethod.getDeclaringModule(),
527-
null,
528-
name,
529-
objectMetaClass,
530-
foundDeclaringModule,
531-
currentMethod.getDeclarationContext(),
532-
currentMethod.getActiveRefinements());
533-
}
534-
535-
536-
@TruffleBoundary
537-
private static MethodLookupResult lookupSuperMethod(RubyModule declaringModule, RubyModule lookupTo,
538-
String name, RubyModule objectMetaClass, Memo<Boolean> foundDeclaringModule,
539-
DeclarationContext declarationContext, DeclarationContext callerDeclaringContext) {
540-
final ArrayList<Assumption> assumptions = new ArrayList<>();
541-
final boolean isRefinedMethod = declaringModule.fields.isRefinement();
478+
var foundDeclaringModule = new Memo<>(false);
479+
var declaringModule = currentMethod.getDeclaringModule();
480+
var declarationContext = currentMethod.getDeclarationContext();
481+
var assumptions = new ArrayList<Assumption>();
542482

483+
// First we need to skip all ancestors until we find declaringModule,
484+
// and then we return the first ancestor after declaringModule which has the method defined.
543485
for (RubyModule ancestor : objectMetaClass.fields.ancestors()) {
544-
if (ancestor == lookupTo) {
545-
return new MethodLookupResult(null, toArray(assumptions));
546-
}
547-
548-
final RubyModule[] refinements = getRefinementsFor(declarationContext, callerDeclaringContext, ancestor);
549-
486+
var refinements = getRefinementsFor(declarationContext, currentMethod.getActiveRefinements(), ancestor);
550487
if (refinements != null) {
551488
for (RubyModule refinement : refinements) {
552-
final MethodLookupResult superMethodInRefinement = lookupSuperMethod(
553-
declaringModule,
554-
ancestor,
555-
name,
556-
refinement,
557-
foundDeclaringModule,
558-
null,
559-
null);
560-
for (Assumption assumption : superMethodInRefinement.getAssumptions()) {
561-
assumptions.add(assumption);
562-
}
563-
if (superMethodInRefinement.isDefined()) {
564-
InternalMethod method = superMethodInRefinement.getMethod();
489+
var refinedMethod = lookupSuperMethodInModule(declaringModule, name, foundDeclaringModule,
490+
refinement, assumptions);
491+
if (refinedMethod != null) {
565492
return new MethodLookupResult(
566-
rememberUsedRefinements(method, declarationContext, refinements, ancestor),
493+
rememberUsedRefinements(refinedMethod, declarationContext, refinements, ancestor),
567494
toArray(assumptions));
568495
}
569-
if (foundDeclaringModule.get() && isRefinedMethod) {
496+
if (foundDeclaringModule.get() && declaringModule.fields.isRefinement()) {
570497
// if method is defined in refinement module (R)
571-
// we should lookup only in this active refinement and skip other
498+
// we should lookup only in this active refinement and skip others
572499
break;
573500
}
574501
}
575502
}
576503

577-
if (!foundDeclaringModule.get()) {
578-
if (ancestor == declaringModule) {
579-
// The declaring module's assumption needs to appended for cases where a newly included module
580-
// should invalidate previous super lookups.
581-
ancestor.fields.getMethodAndAssumption(name, assumptions);
582-
foundDeclaringModule.set(true);
583-
}
584-
} else {
585-
final ModuleFields fields = ancestor.fields;
586-
final InternalMethod method = fields.getMethodAndAssumption(name, assumptions);
587-
if (method != null) {
588-
return new MethodLookupResult(method, toArray(assumptions));
589-
}
504+
var method = lookupSuperMethodInModule(declaringModule, name, foundDeclaringModule, ancestor, assumptions);
505+
if (method != null) {
506+
return new MethodLookupResult(method, toArray(assumptions));
590507
}
591508
}
592509

593510
// Nothing found
594511
return new MethodLookupResult(null, toArray(assumptions));
595512
}
596513

514+
515+
private static InternalMethod lookupSuperMethodInModule(RubyModule declaringModule, String name,
516+
Memo<Boolean> foundDeclaringModule, RubyModule module, ArrayList<Assumption> assumptions) {
517+
if (!foundDeclaringModule.get()) {
518+
if (module == declaringModule) {
519+
// The declaring module's assumption needs to appended for cases where a newly included module
520+
// should invalidate previous super lookups.
521+
module.fields.getMethodAndAssumption(name, assumptions);
522+
foundDeclaringModule.set(true);
523+
}
524+
return null;
525+
} else {
526+
return module.fields.getMethodAndAssumption(name, assumptions);
527+
}
528+
}
529+
597530
private static InternalMethod rememberUsedRefinements(InternalMethod method,
598531
DeclarationContext declarationContext) {
599532
return method.withActiveRefinements(declarationContext);
@@ -603,8 +536,7 @@ private static InternalMethod rememberUsedRefinements(InternalMethod method,
603536
DeclarationContext declarationContext, RubyModule[] refinements, RubyModule ancestor) {
604537
assert refinements != null;
605538

606-
final Map<RubyModule, RubyModule[]> currentRefinements = new HashMap<>(
607-
declarationContext.getRefinements());
539+
final Map<RubyModule, RubyModule[]> currentRefinements = new HashMap<>(declarationContext.getRefinements());
608540
currentRefinements.put(ancestor, refinements);
609541

610542
return method.withActiveRefinements(declarationContext.withRefinements(currentRefinements));

test/mri/excludes/TestRefinement.rb

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,12 @@
1-
exclude :test_eval_with_binding_scoping, "needs investigation"
2-
exclude :test_include_refinement, "needs investigation"
3-
exclude :test_prepend_after_refine, "needs investigation"
4-
exclude :test_refine_prepended_class, "needs investigation"
5-
exclude :test_refine_with_proc, "needs investigation"
6-
exclude :test_undef_original_method, "needs investigation"
7-
exclude :test_warn_setconst_in_refinmenet, "needs investigation"
8-
exclude :test_refine_in_using, "needs investigation"
9-
exclude :test_used_modules, "needs investigation"
10-
exclude :test_unbound_refine_method, "needs investigation"
111
exclude :test_ancestors, "[ruby-core:86949] [Bug #14744]."
12-
exclude :test_import_methods, "NoMethodError: undefined method `bar' for #<TestRefinement::TestImport::A:0x17bee78>"
2+
exclude :test_eval_with_binding_scoping, "pid 123017 exit 0."
3+
exclude :test_import_methods, "ArgumentError expected but nothing was raised."
4+
exclude :test_prepend_after_refine, "<\"refined\"> expected but was"
5+
exclude :test_refine_prepended_class, "<[:c, :m1, :m2]> expected but was"
6+
exclude :test_refine_with_proc, "ArgumentError expected but nothing was raised."
7+
exclude :test_unbound_refine_method, "TypeError expected but nothing was raised."
8+
exclude :test_used_modules, "<[TestRefinement::VisibleRefinements::RefB,"
139
exclude :test_refinements, "TruffleRuby does not guarantee refinement list ordering"
14-
exclude :test_refined_class, "TruffleRuby does not guarantee refinement list ordering"
15-
exclude :test_prepend_into_refinement, "TypeError expected but nothing was raised."
16-
exclude :test_include_into_refinement, "TypeError expected but nothing was raised."
17-
exclude :test_refined_protected_methods, "assert_separately failed with error message"
10+
exclude :test_warn_setconst_in_refinmenet, "[ruby-core:64143] [Bug #10103]"
11+
exclude :test_refine_in_using, "NoMethodError: undefined method `foo' for #<TestRefinement::RefineInUsing:0xd88>"
12+
exclude :test_refined_protected_methods, "NoMethodError: protected method `foo' called for #<C:0x2c8>"

0 commit comments

Comments
 (0)