blob: f930cb3c47fa710f79e3fafd154feecae6f88e8c [file] [log] [blame]
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001// Copyright 2014 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14package com.google.devtools.build.lib.syntax;
15
16import com.google.common.collect.Iterables;
17import com.google.devtools.build.lib.events.Location;
18import com.google.devtools.build.lib.syntax.FuncallExpression.MethodDescriptor;
19
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010020import java.util.List;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010021
22/**
23 * Syntax node for a dot expression.
24 * e.g. obj.field, but not obj.method()
25 */
26public final class DotExpression extends Expression {
27
28 private final Expression obj;
29
Florian Weikert6f864c32015-07-23 11:26:39 +000030 private final Identifier field;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010031
Florian Weikert6f864c32015-07-23 11:26:39 +000032 public DotExpression(Expression obj, Identifier field) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010033 this.obj = obj;
34 this.field = field;
35 }
36
37 public Expression getObj() {
38 return obj;
39 }
40
Florian Weikert6f864c32015-07-23 11:26:39 +000041 public Identifier getField() {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010042 return field;
43 }
44
45 @Override
46 public String toString() {
47 return obj + "." + field;
48 }
49
50 @Override
Florian Weikert90a15962015-09-11 13:43:10 +000051 Object doEval(Environment env) throws EvalException, InterruptedException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010052 Object objValue = obj.eval(env);
53 String name = field.getName();
54 Object result = eval(objValue, name, getLocation());
55 if (result == null) {
56 if (objValue instanceof ClassObject) {
57 String customErrorMessage = ((ClassObject) objValue).errorMessage(name);
58 if (customErrorMessage != null) {
59 throw new EvalException(getLocation(), customErrorMessage);
60 }
61 }
Francois-Rene Rideau8d5cce32015-06-16 23:12:04 +000062 throw new EvalException(getLocation(), Printer.format("Object of type '%s' has no field %r",
63 EvalUtils.getDataTypeName(objValue), name));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010064 }
65 return result;
66 }
67
68 /**
69 * Returns the field of the given name of the struct objValue, or null if no such field exists.
70 */
71 public static Object eval(Object objValue, String name, Location loc) throws EvalException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010072 if (objValue instanceof ClassObject) {
Florian Weikert3f610e82015-08-18 14:37:46 +000073 Object result = null;
74 try {
75 result = ((ClassObject) objValue).getValue(name);
76 } catch (IllegalArgumentException ex) {
77 throw new EvalException(loc, ex);
78 }
Florian Weikerteee8be62015-07-30 13:28:26 +000079 // ClassObjects may have fields that are annotated with @SkylarkCallable.
80 // Since getValue() does not know about those, we cannot expect that result is a valid object.
81 if (result != null) {
82 result = SkylarkType.convertToSkylark(result, loc);
83 // If we access NestedSets using ClassObject.getValue() we won't know the generic type,
84 // so we have to disable it. This should not happen.
85 SkylarkType.checkTypeAllowedInSkylark(result, loc);
86 return result;
87 }
Laurent Le Brun427bd972015-05-20 13:28:44 +000088 }
Florian Weikerteee8be62015-07-30 13:28:26 +000089
90 List<MethodDescriptor> methods =
91 FuncallExpression.getMethods(objValue.getClass(), name, 0, loc);
Laurent Le Brun427bd972015-05-20 13:28:44 +000092 if (methods != null && !methods.isEmpty()) {
93 MethodDescriptor method = Iterables.getOnlyElement(methods);
94 if (method.getAnnotation().structField()) {
95 return FuncallExpression.callMethod(method, name, objValue, new Object[] {}, loc);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010096 }
97 }
Laurent Le Brun427bd972015-05-20 13:28:44 +000098 return null;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010099 }
100
101 @Override
102 public void accept(SyntaxTreeVisitor visitor) {
103 visitor.visit(this);
104 }
105
106 @Override
Laurent Le Brun2e78d612015-04-15 09:06:46 +0000107 void validate(ValidationEnvironment env) throws EvalException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100108 obj.validate(env);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100109 }
110}