Add support for --enable_type_checks (developer mode) for dartc.

This patch is based on https://chromereviews.googleplex.com/3520019/.  However it addresses the following issues:

1) TypeError exceptions will now have the stack trace filled in.
2) Type parameters are now accessible from closurized code, including the trampoline shims for named optinal parameters.
3) Suppress use of FunctionExpressionInliner with type checks because the inline normalization can leave return nodes in the AST.
4) Pass the generateHumanReadableOutput flag to the closure backend.
5) TypeDartcTest passes in debug and release modes with type checking enabled.  We still need to enable the additional cases in the code.

Review URL: https://chromereviews.googleplex.com/3558021

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@271 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
mmendez@google.com 2011-10-08 04:11:34 +00:00
parent 179f836439
commit fc7390d97b
19 changed files with 901 additions and 48 deletions

View file

@ -38,6 +38,9 @@ public class CommandLineOptions {
usage = "do not generate output, only analyze")
private boolean checkOnly = false;
@Option(name = "--enable_type_checks", usage = "generate runtime type checks")
private boolean developerModeChecks = false;
@Option(name = "--disable-type-optimizations",
usage = "Debugging: disable type optimizations")
private boolean disableTypeOptimizations = false;
@ -252,6 +255,10 @@ public class CommandLineOptions {
public boolean warningsAreFatal() {
return warningsAreFatal;
}
public boolean developerModeChecks() {
return developerModeChecks;
}
}
/**
@ -316,7 +323,7 @@ public class CommandLineOptions {
/**
* Command line options accepted by the {@link TestRunner} entry point.
* Command line options accepted by the {@link com.google.dart.runner.TestRunner} entry point.
*/
public static class TestRunnerOptions extends DartRunnerOptions {
}
@ -329,8 +336,7 @@ public class CommandLineOptions {
* for 'not a valid option' are suppressed.
*
* @param args Arguments passed from main()
* @param cmdLineParser An initialized {@link CmdLineParser} for the desired
* argument set.
* @param parsedOptions [out parameter] parsed options
* @throws CmdLineException Thrown if there is a problem parsing the options.
*/
public static CmdLineParser parse(String[] args, CompilerOptions parsedOptions)

View file

@ -20,6 +20,12 @@ public interface CompilerConfiguration {
List<Backend> getBackends();
/**
* Indicates whether developer-mode runtime checks are needed.
* @return true if developer-mode checks should be inserted, false if not
*/
boolean developerModeChecks();
/**
* Returns <code>true</code> if the compiler's output should be optimized.
*/

View file

@ -128,6 +128,11 @@ public class DefaultCompilerConfiguration implements CompilerConfiguration {
return backends;
}
@Override
public boolean developerModeChecks() {
return compilerOptions.developerModeChecks();
}
@Override
public boolean shouldOptimize() {
return compilerOptions.shouldOptimize();

View file

@ -22,6 +22,12 @@ public class DelegatingCompilerConfiguration implements CompilerConfiguration {
public DelegatingCompilerConfiguration(CompilerConfiguration delegate) {
this.delegate = delegate;
}
@Override
public boolean developerModeChecks() {
return delegate.developerModeChecks();
}
@Override
public List<DartCompilationPhase> getPhases() {
return delegate.getPhases();

View file

@ -331,8 +331,8 @@ public abstract class AbstractJsBackend extends AbstractBackend {
unit.getSourceName()) : null;
try {
// Normalize front-end AST for back-end consumption.
unit = (DartUnit) (new Normalizer()).exec(unit, typeProvider, optimizationStrategy)
.getNormalizedNode();
unit = (DartUnit) (new Normalizer()).exec(unit, typeProvider,
optimizationStrategy).getNormalizedNode();
} finally {
Tracer.end(normalizeEvent);
}

View file

@ -491,6 +491,12 @@ public class ClosureJsBackend extends AbstractJsBackend {
// options.setShadowVariables(false);
// options.inlineFunctions = false;
/*
* NOTE: We turn this off because TypeErrors or anything that relies on a type name will fail
* due to the renaming.
*/
options.setReplaceIdGenerators(false);
return options;
}

View file

@ -269,7 +269,8 @@ public class GenerateJavascriptAST {
// setup the global scope.
jsNewDeclarationsStack.push(new HashSet<JsName>());
currentHolder = unit.getLibrary().getElement();
rtt = new RuntimeTypeInjector(this, typeProvider, translationContext);
rtt = new RuntimeTypeInjector(this, typeProvider, translationContext,
context.getCompilerConfiguration().developerModeChecks());
}
/**
@ -958,7 +959,7 @@ public class GenerateJavascriptAST {
private JsNode generateConstructorDefinition(DartMethodDefinition x) {
assert currentScopeInfo == null : "Nesting a constructor in a method should be impossible";
currentScopeInfo = ScopeRootInfo.makeScopeInfo(x);
currentScopeInfo = ScopeRootInfo.makeScopeInfo(x, !shouldGenerateDeveloperModeChecks());
ConstructorElement element = (ConstructorElement) x.getSymbol();
ClassElement classElement = (ClassElement) element.getEnclosingElement();
JsScope classMemberScope = translationContext.getMemberScopes().get(classElement);
@ -1088,7 +1089,7 @@ public class GenerateJavascriptAST {
assert currentScopeInfo == null : "Nested methods should be impossible";
inFactoryOrStaticContext = x.getModifiers().isFactory()
|| x.getModifiers().isStatic();
currentScopeInfo = ScopeRootInfo.makeScopeInfo(x);
currentScopeInfo = ScopeRootInfo.makeScopeInfo(x, !shouldGenerateDeveloperModeChecks());
JsFunction func = (JsFunction) generate(x.getFunction());
@ -1555,7 +1556,7 @@ public class GenerateJavascriptAST {
JsExprStmt result = null;
if (initializer != null || Elements.isTopLevel(element)) {
currentScopeInfo = ScopeRootInfo.makeScopeInfo(x);
currentScopeInfo = ScopeRootInfo.makeScopeInfo(x, !shouldGenerateDeveloperModeChecks());
inFactoryOrStaticContext = true;
// There's an initializer, so emit an assignment statement.
@ -1632,6 +1633,24 @@ public class GenerateJavascriptAST {
}
jsFunc.setBody(body);
// Create JS parameters
List<DartParameter> params = x.getParams();
List<JsParameter> jsParams = jsFunc.getParameters();
generateAll(params, jsParams, JsParameter.class);
// Create the runtime type checks that will be inserted later
List<JsStatement> checks = Lists.newArrayList();
int numParams = params.size();
for (int i = 0; i < numParams; ++i) {
JsNameRef jsParam = jsParams.get(i).getName().makeRef();
JsExpression expr = rtt.addTypeCheck(getCurrentClass(), jsParam,
typeOf(params.get(i).getTypeNode()), null, params.get(i));
if (expr != jsParam) {
// if the expression was returned unchanged, omit the check
checks.add(new JsExprStmt(expr));
}
}
// Add temporary variable declarations, if any.
declareTempsInBlock(body, jsNewDeclarationsStack.pop());
@ -1654,16 +1673,29 @@ public class GenerateJavascriptAST {
maybeAddFunctionScopeAlias(currentScopeInfo.getScope(x),
translationContext.getMethods().get(x));
// 2. setup parameter values
generateAll(x.getParams(), jsFunc.getParameters(), JsParameter.class);
// 1. call function trace before anything else.
// 2. call function trace before anything else.
maybeAddFunctionTracing(x);
// 1. insert parameter type checks at the beginning of the method
body.getStatements().addAll(0, checks);
functionStack.pop();
return jsFunc.setSourceRef(x);
}
/**
* Get the type associated with a type node
*
* @param typeNode a {@link DartTypeNode}, which may be null
* @return a {@link Type} corresponding to the type node or null to indicate unknown
*/
private Type typeOf(DartTypeNode typeNode) {
if (typeNode == null) {
return null;
}
return typeNode.getType();
}
private boolean isFactory(DartMethodDefinition method) {
return method.getModifiers().isFactory();
}
@ -1880,8 +1912,17 @@ public class GenerateJavascriptAST {
@Override
public JsNode visitReturnStatement(DartReturnStatement x) {
JsReturn jsRet = new JsReturn();
if (x.getValue() != null) {
jsRet.setExpr((JsExpression) generate(x.getValue()));
DartExpression returnValue = x.getValue();
if (returnValue != null) {
JsExpression expr = (JsExpression) generate(returnValue);
DartFunction function = functionStack.peek();
if (function != null) {
// NOTE: FunctionExpressionInliner might be leaving return statements around
DartTypeNode returnType = function.getReturnTypeNode();
expr = rtt.addTypeCheck(getCurrentClass(), expr, typeOf(returnType),
returnValue.getType(), x);
}
jsRet.setExpr(expr);
}
return jsRet.setSourceRef(x);
}
@ -2016,9 +2057,12 @@ public class GenerateJavascriptAST {
// If the name is referenced by a closure use the scope alias.
JsNameRef scopeAliasRef = maybeMakeScopeAliasReference(targetSymbol);
JsNode result = null;
DartExpression value = x.getValue();
if (scopeAliasRef != null) {
if (x.getValue() != null) {
JsExpression initExpr = (JsExpression) generate(x.getValue());
if (value != null) {
JsExpression initExpr = (JsExpression) generate(value);
Type type = getTypeOfIdentifier(x.getName());
initExpr = rtt.addTypeCheck(getCurrentClass(), initExpr, type, value.getType(), x);
result = AstUtil.newAssignment(scopeAliasRef, initExpr).setSourceRef(x).makeStmt();
} else {
// we need to put some statement in to keep the expected number
@ -2027,8 +2071,10 @@ public class GenerateJavascriptAST {
}
} else {
JsVars.JsVar jsVar = new JsVars.JsVar(getJsName(targetSymbol));
if (x.getValue() != null) {
JsExpression initExpr = (JsExpression) generate(x.getValue());
if (value != null) {
JsExpression initExpr = (JsExpression) generate(value);
Type type = getTypeOfIdentifier(x.getName());
initExpr = rtt.addTypeCheck(getCurrentClass(), initExpr, type, value.getType(), x);
jsVar.setInitExpr(initExpr);
} else {
jsVar.setInitExpr(undefined());
@ -2106,9 +2152,10 @@ public class GenerateJavascriptAST {
return generateInstanceOfComparison(x);
}
JsExpression rhs = (JsExpression) generate(x.getArg2());
DartExpression arg2 = x.getArg2();
JsExpression rhs = (JsExpression) generate(arg2);
if (operator == Token.ASSIGN) {
return x.getArg1().accept(new Assignment(x, rhs));
return x.getArg1().accept(new Assignment(x, rhs, arg2.getType()));
}
assert !operator.isUserDefinableOperator() || !operator.isAssignmentOperator() : x;
@ -2129,7 +2176,7 @@ public class GenerateJavascriptAST {
// TODO (fabiomfv) - This optimization targets a v8 perf issue. V8 double equals
// comparison to undefined is up to 4 times slower than == null. It seems that it was
// fixed on v8 3.5. once we move to 3.5 and the fix confirmed, this should be revisited.
if (x.getArg2() instanceof DartNullLiteral) {
if (arg2 instanceof DartNullLiteral) {
op = mapToNonStrictEquals(op);
rhs = nulle();
}
@ -2150,6 +2197,41 @@ public class GenerateJavascriptAST {
}
}
private Type getTypeOfIdentifier(DartIdentifier ident) {
Element element = ident.getReferencedElement();
DartTypeNode typeNode = null;
if (element == null) {
DartNode parent = ident.getParent();
if (parent instanceof DartVariable) {
DartVariableStatement varStmt = (DartVariableStatement) parent.getParent();
typeNode = varStmt.getTypeNode();
} else {
throw new InternalCompilerException("Unexpected identifier type: " + ident);
}
} else {
switch (element.getKind()) {
case VARIABLE:
DartVariableStatement varStmt = (DartVariableStatement) element.getNode().getParent();
typeNode = varStmt.getTypeNode();
break;
case PARAMETER:
DartParameter param = (DartParameter) element.getNode();
typeNode = param.getTypeNode();
break;
case FIELD:
DartFieldDefinition fieldDef = (DartFieldDefinition) element.getNode().getParent();
typeNode = fieldDef.getTypeNode();
break;
default:
throw new InternalCompilerException("Unexpected identifier element type " + element);
}
}
if (typeNode != null) {
return typeNode.getType();
}
return null;
}
private JsExpression generateInstanceOfComparison(DartBinaryExpression x) {
JsExpression lhs = (JsExpression) generate(x.getArg1());
DartExpression rhs = x.getArg2();
@ -2335,7 +2417,9 @@ public class GenerateJavascriptAST {
DartExpression target = x.getTarget();
if (target instanceof DartFunctionExpression) {
DartFunctionExpression functionExpression = (DartFunctionExpression) target;
if (functionExpression.getSymbol().getModifiers().isInlinable()) {
if (functionExpression.getSymbol().getModifiers().isInlinable() &&
!shouldGenerateDeveloperModeChecks()) {
// TODO FunctionExpressionInliner conflics with developer mode checks
return new FunctionExpressionInliner(functionExpression, x.getArgs()).call();
}
}
@ -2707,6 +2791,18 @@ public class GenerateJavascriptAST {
return intern;
}
private boolean shouldBindThis(ScopeRootInfo.ClosureInfo info) {
if (shouldGenerateDeveloperModeChecks()) {
return true;
}
return !inFactoryOrStaticContext && info.referencesThis;
}
private boolean shouldGenerateDeveloperModeChecks() {
return context.getCompilerConfiguration().developerModeChecks();
}
@Override
public JsNode visitFunctionExpression(DartFunctionExpression x) {
JsFunction fn = (JsFunction) generate(x.getFunction());
@ -2751,7 +2847,7 @@ public class GenerateJavascriptAST {
if (!fnWasPreviouslyHoisted) {
// Generate the named-parameter trampoline.
boolean includesClosureScope = !list.isEmpty();
boolean preserveThis = !inFactoryOrStaticContext && info.referencesThis;
boolean preserveThis = shouldBindThis(info);
JsFunction tramp = generateNamedParameterTrampoline(x.getFunction(),
hoistedName.makeRef(),
list.size(), preserveThis);
@ -2775,7 +2871,7 @@ public class GenerateJavascriptAST {
JsExpression thisRef = undefined();
// Only bind 'this' if 'this' is referenced
if (!inFactoryOrStaticContext && info.referencesThis) {
if (shouldBindThis(info)) {
thisRef = new JsThisRef();
}
@ -3161,10 +3257,12 @@ public class GenerateJavascriptAST {
class Assignment extends DartNodeTraverser<JsNode> {
private final DartNode info;
private final JsExpression rhs;
private final Type rhsType;
public Assignment(DartNode info, JsExpression rhs) {
public Assignment(DartNode info, JsExpression rhs, Type rhsType) {
this.info = info;
this.rhs = rhs;
this.rhsType = rhsType;
}
@Override
@ -3178,16 +3276,21 @@ public class GenerateJavascriptAST {
if (lhs != normalizedNode) {
return normalizedNode.accept(this);
}
Type type = getTypeOfIdentifier(lhs);
JsExpression wrapped = rtt.addTypeCheck(getCurrentClass(), rhs, type, rhsType, info);
Element element = optStrategy.findOptimizableFieldElementFor(lhs, FieldKind.SETTER);
// On the form e1.name = rhs.
return generateStore(null, lhs, rhs, element).setSourceRef(info);
return generateStore(null, lhs, wrapped, element).setSourceRef(info);
}
@Override
public JsNode visitPropertyAccess(DartPropertyAccess lhs) {
Element element = optStrategy.findOptimizableFieldElementFor(lhs, FieldKind.SETTER);
// On the form e1.name = rhs.
return generateStore(lhs.getQualifier(), lhs.getName(), rhs, element).setSourceRef(info);
Type type = lhs.getType();
JsExpression wrapped = rtt.addTypeCheck(getCurrentClass(), rhs, type, rhsType, info);
return generateStore(lhs.getQualifier(), lhs.getName(), wrapped,
element).setSourceRef(info);
}
@Override
@ -3197,16 +3300,18 @@ public class GenerateJavascriptAST {
JsExpression key = (JsExpression) generate(lhs.getKey());
JsExpression e1 = (JsExpression) generate(lhs.getTarget());
Type type = lhs.getType();
JsExpression wrapped = rtt.addTypeCheck(getCurrentClass(), rhs, type, rhsType, info);
if (optStrategy.canSkipArrayAccessShim(lhs, true /* isAssignee */)) {
JsBinaryOperation assign = new JsBinaryOperation(JsBinaryOperator.ASG);
assign.setArg1(AstUtil.newArrayAccess(e1, inlineArrayIndexCheck(e1, key)));
assign.setArg2(rhs);
assign.setArg2(wrapped);
return assign.setSourceRef(info);
} else {
JsNameRef $0 = new JsNameRef(createTemporary());
String $set = mangler.createOperatorSyntax(Token.ASSIGN_INDEX);
// Generate: $0 = rhs
JsExpression e = AstUtil.newAssignment($0, rhs);
JsExpression e = AstUtil.newAssignment($0, wrapped);
// Generate: e1.$set(key, $0 = rhs)
e = AstUtil.newInvocation(AstUtil.newNameRef(e1, $set), key, e);
// Generate: e, $0

View file

@ -57,6 +57,7 @@ import com.google.dart.compiler.resolver.ClassElement;
import com.google.dart.compiler.resolver.ConstructorElement;
import com.google.dart.compiler.resolver.CoreTypeProvider;
import com.google.dart.compiler.resolver.ElementKind;
import com.google.dart.compiler.type.DynamicType;
import com.google.dart.compiler.type.FunctionType;
import com.google.dart.compiler.type.InterfaceType;
import com.google.dart.compiler.type.Type;
@ -76,6 +77,7 @@ import java.util.Set;
* @author johnlenz@google.com (John Lenz)
*/
public class RuntimeTypeInjector {
private final boolean emitTypeChecks;
private final TraversalContextProvider context;
private final JsBlock globalBlock;
private final JsScope globalScope;
@ -87,7 +89,7 @@ public class RuntimeTypeInjector {
RuntimeTypeInjector(
TraversalContextProvider context,
CoreTypeProvider typeProvider,
TranslationContext translationContext) {
TranslationContext translationContext, boolean emitTypeChecks) {
this.context = context;
this.translationContext = translationContext;
JsProgram program = translationContext.getProgram();
@ -95,6 +97,7 @@ public class RuntimeTypeInjector {
this.globalScope = program.getScope();
this.builtinTypes = makeBuiltinTypes(typeProvider);
this.typeProvider = typeProvider;
this.emitTypeChecks = emitTypeChecks;
}
private Map<ClassElement, String> makeBuiltinTypes(CoreTypeProvider typeProvider) {
@ -744,8 +747,7 @@ public class RuntimeTypeInjector {
return call(src, nameref(src, rtt, "implementedBy"), lhs);
}
private JsExpression generateRefiedTypeVariableComparison(
JsExpression lhs, TypeVariable type, ClassElement contextClassElement, SourceInfo src) {
private JsExpression getReifiedTypeVariableRTT(TypeVariable type, ClassElement contextClassElement) {
JsExpression rttContext;
if (!inFactory()) {
// build: this.typeinfo.implementedTypes['class'].typeArgs;
@ -756,6 +758,12 @@ public class RuntimeTypeInjector {
}
// rtt = rttContext.typeArgs[x]
JsExpression rtt = buildTypeLookupExpression(type, contextClassElement.getTypeParameters(), rttContext);
return rtt;
}
private JsExpression generateRefiedTypeVariableComparison(
JsExpression lhs, TypeVariable type, ClassElement contextClassElement, SourceInfo src) {
JsExpression rtt = getReifiedTypeVariableRTT(type, contextClassElement);
return call(src, nameref(src, rtt, "implementedBy"), lhs);
}
@ -806,4 +814,128 @@ public class RuntimeTypeInjector {
|| member.getModifiers().isFactory()
|| member.getModifiers().isStatic();
}
/**
* Optionally emit a runtime type check, which is a call to $chk passing the
* runtime type object for the required type and an expression.
*
* @param enclosingClass enclosing class element
* @param expr expression to check
* @param type {@link Type} to check against, null is unknown
* @param src source info to use for the generated code
* @return an expression wrapping the type check call
*/
JsExpression addTypeCheck(ClassElement enclosingClass, JsExpression expr, Type type,
Type exprType, SourceInfo src) {
if (!emitTypeChecks || isStaticallyGoodAssignment(type, exprType)) {
return expr;
}
if (isStaticallyBadAssignment(type, exprType, expr)) {
return injectTypeError(enclosingClass, expr, type, src);
}
return injectTypeCheck(enclosingClass, expr, type, src);
}
/**
* Check if an assignment is statically known to have a type error.
*
* @param type
* @param exprType
* @param expr
* @return true if the assignment is known to be bad statically
*/
private boolean isStaticallyBadAssignment(Type type, Type exprType, JsExpression expr) {
if (!expr.isDefinitelyNotNull()) {
// nulls can be assigned to any type, so if this may be null we can't assume it is a bad
// assignment
return false;
}
// TODO(jat): implement static subtype checks
return false;
}
/**
* @param type
* @param exprType
* @return true if the assignment is statically known to not need a check
*/
private boolean isStaticallyGoodAssignment(Type type, Type exprType) {
if (type == null || type instanceof DynamicType) {
// if there is no target type or it is dynamic, it is good
return true;
}
// TODO(jat): implement static subtype checks
return false;
}
/**
* Optionally emit a type error, used when it is statically known that a
* TypeError must be thrown.
*
* @param enclosingClass enclosing class element
* @param expr expression that fails the check
* @param type type to check against, null is unknown
* @param src source info to use for the generated code
* @return an expression wrapping the call to the throw helper
*/
private JsExpression injectTypeError(ClassElement enclosingClass, JsExpression expr, Type type,
SourceInfo src) {
JsExpression rtt = getRtt(enclosingClass, expr, type);
if (rtt != null) {
expr = call(src, nameref(src, "$te"), rtt, expr);
}
return expr;
}
/**
* Optionally emit a runtime type check, which is a call to $chk passing the
* runtime type object for the required type and an expression.
*
* @param enclosingClass enclosing class element
* @param expr expression to check
* @param type type to check against, null is unknown
* @param src source info to use for the generated code
* @return an expression wrapping the type check call
*/
private JsExpression injectTypeCheck(ClassElement enclosingClass, JsExpression expr, Type type,
SourceInfo src) {
JsExpression rtt = getRtt(enclosingClass, expr, type);
if (rtt != null) {
expr = call(src, nameref(src, "$chk"), rtt, expr);
}
return expr;
}
/**
* Get the runtime type object for a type.
*
* @param enclosingClass
* @param expr
* @param type
* @return a {@link JsExpression} for the runtime type, or null if no check is required/possible
*/
private JsExpression getRtt(ClassElement enclosingClass, JsExpression expr, Type type) {
if (!emitTypeChecks || type == null) {
return null;
}
JsExpression rtt;
switch (TypeKind.of(type)) {
case INTERFACE:
InterfaceType interfaceType = (InterfaceType) type;
// TODO(jat): do we need a special case for raw interfaces?
return generateRTTLookup(interfaceType, enclosingClass);
case VARIABLE:
TypeVariable typeVar = (TypeVariable) type;
return getReifiedTypeVariableRTT(typeVar, enclosingClass);
case VOID:
case FUNCTION_ALIAS:
// TODO(jat): implement, no checks for now
case DYNAMIC:
// no check required
return null;
default:
throw new IllegalStateException("unexpected type " + type);
}
}
}

View file

@ -25,6 +25,7 @@ import com.google.dart.compiler.ast.DartMethodDefinition;
import com.google.dart.compiler.ast.DartNode;
import com.google.dart.compiler.ast.DartParameter;
import com.google.dart.compiler.ast.DartThisExpression;
import com.google.dart.compiler.ast.DartTypeParameter;
import com.google.dart.compiler.ast.DartUnit;
import com.google.dart.compiler.ast.DartVariable;
import com.google.dart.compiler.backend.js.ast.JsName;
@ -308,9 +309,11 @@ class ScopeRootInfo {
private Deque<DartScope> scopeStack = Lists.newLinkedList();
private final Map<DartFunction, ScopeRootInfo.ClosureInfo> closures = Maps.newHashMap();
private Deque<DartFunction> closureStack = Lists.newLinkedList();
ClosureRefenceMapBuilder(Map<DartNode, DartScope> scopes) {
private final boolean respectInlinableModifier;
ClosureRefenceMapBuilder(Map<DartNode, DartScope> scopes, boolean respectInlinableModifier) {
this.scopes = scopes;
this.respectInlinableModifier = respectInlinableModifier;
}
@Override
@ -329,7 +332,8 @@ class ScopeRootInfo {
DartExpression target = x.getTarget();
if (target instanceof DartFunctionExpression) {
DartFunctionExpression functionExpression = (DartFunctionExpression) target;
if (functionExpression.getSymbol().getModifiers().isInlinable()) {
if (respectInlinableModifier &&
functionExpression.getSymbol().getModifiers().isInlinable()) {
acceptList(x.getArgs());
return traverseFunction(functionExpression.getFunction(), ctx);
}
@ -471,19 +475,20 @@ class ScopeRootInfo {
private final Map<DartNode, DartScope> scopes;
private int closureIds = 0;
static ScopeRootInfo makeScopeInfo(DartMethodDefinition x) {
return makeScopeInfoImpl(x);
static ScopeRootInfo makeScopeInfo(DartMethodDefinition x, boolean respectInlinableModifier) {
return makeScopeInfoImpl(x, respectInlinableModifier);
}
static ScopeRootInfo makeScopeInfo(DartField x) {
return makeScopeInfoImpl(x);
static ScopeRootInfo makeScopeInfo(DartField x, boolean respectInlinableModifier) {
return makeScopeInfoImpl(x, respectInlinableModifier);
}
private static ScopeRootInfo makeScopeInfoImpl(DartClassMember<?> x) {
private static ScopeRootInfo makeScopeInfoImpl(DartClassMember<?> x,
boolean respectInlinableModifier) {
ScopeRootInfo.MethodScopeMapBuilder scopeBuilder = new MethodScopeMapBuilder();
scopeBuilder.accept(x);
ScopeRootInfo.ClosureRefenceMapBuilder closureBuilder = new ClosureRefenceMapBuilder(
scopeBuilder.scopes);
scopeBuilder.scopes, respectInlinableModifier);
closureBuilder.accept(x);
return new ScopeRootInfo(x, scopeBuilder.scopes, closureBuilder.closures);
}
@ -525,4 +530,4 @@ class ScopeRootInfo {
public String getNextClosureName() {
return "c" + closureIds++;
}
}
}

View file

@ -25,6 +25,11 @@ import java.util.List;
public class TestCompilerConfiguration implements CompilerConfiguration {
private final SystemLibraryManager systemLibraryManager = new SystemLibraryManager();
@Override
public boolean developerModeChecks() {
return false;
}
@Override
public boolean warningsAreFatal() {
return false;

View file

@ -311,7 +311,7 @@ public class DartRunner {
final DartRunnerOptions options, DartCompilerListener listener) throws RunnerError {
Backend backend;
if (options.shouldOptimize()) {
backend = new ClosureJsBackend();
backend = new ClosureJsBackend(options.generateHumanReadableOutput());
} else {
backend = new JavascriptBackend();
}

View file

@ -2,17 +2,33 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// Exceptions thrown by the VM.
// Exceptions thrown by the VM -- user code should never throw these directly.
// TODO(jat): these should be compatible with the definitions in runtime/lib/error.dart
class AssertionError {
const AssertionError();
String toString() {
return "Failed assertion";
}
}
class TypeError extends AssertionError {
const TypeError() : super();
final String dstType;
final String srcType;
const TypeError(this.srcType, this.dstType);
String toString() {
return "Failed type check: type $srcType is not assignable to type $dstType";
}
}
class FallThroughError {
const FallThroughError() : super();
const FallThroughError();
String toString() {
return "Switch case fall-through";
}
}

View file

@ -36,6 +36,10 @@ class ExceptionHelper {
receiver, functionName, arguments) native {
return new NoSuchMethodException(receiver, functionName, arguments);
}
static TypeError createTypeError(String srcType, String dstType) native {
return new TypeError(srcType, dstType);
}
}
class _CoreJsUtil {

View file

@ -184,6 +184,7 @@ var $Dart$Null = void 0;
function assert(expr, msg) {
var val = typeof(expr) == 'function' ? expr() : expr;
if (!val) {
// TODO: throw a Dart AssertionError instead
var err = new Error('Assertion failed. ' + (msg || ''));
Error.captureStackTrace && Error.captureStackTrace(err);
throw err;

View file

@ -75,6 +75,20 @@ RTT.prototype.implementedByType = function(otherType) {
return true;
};
/**
* @return {string} the class name associated with this type
*/
RTT.prototype.getClassName = function() {
var name = this.classKey;
if (name.substr(0, 4) == "cls:") {
name = name.substr(4);
}
if (name.substr(-5) == "$Dart") {
name = name.substr(0, name.length - 5);
}
return name;
}
/**
* @param {RTT}
* @return {boolean}
@ -208,6 +222,35 @@ RTT.placeholderType = new RTT($cls('::'));
RTT.placeholderType.implementedBy = function(o) {return true};
RTT.placeholderType.implementedByType = function(o) {return true};
/**
* Checks that a value is assignable to an expected type, and either returns that
* value if it is, or else throws a TypeMismatchException.
*
* @param {!RTT} the expected type
* @param {*} the value to check
* @return {*} the value
*/
function $chk(rtt, value) {
// null can be assigned to any type
if (value == $Dart$Null || rtt.implementedBy(value)) {
return value;
}
$te(rtt, value);
}
/**
* Throw a TypeError. See core.dart for the ExceptionHelper class.
*
* @param {!RTT} the expected type
* @param {*) the value that failed
*/
function $te(rtt, value) {
var srcType = RTT.getTypeInfo(value).getClassName();
var dstType = rtt.getClassName();
var e = native_ExceptionHelper_createTypeError(srcType, dstType);
$Dart$ThrowException(e);
}
// Setup the Function object
Function.prototype.$implements$Function$Dart = 1;
RTT.setTypeInfo(Function.prototype, RTT.create($cls('Function$Dart')));

View file

@ -2,6 +2,7 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// Errors are created and thrown by DartVM only.
// Changes here should also be reflected in corelib/error.dart as well
class AssertError {
factory AssertError._uninstantiable() {

View file

@ -131,7 +131,6 @@ CharEscapeTest: Fail
FunctionTypeParameter2NegativeTest: Fail # Bug 4568007
# The following tests use missing error classes. Bug 4385894.
TypeTest: Fail # Uses TypeError class.
AssertTest: Fail # Uses AssertError class.
ThirdTest: Fail # Bug 5339586

View file

@ -0,0 +1,513 @@
// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// VMOptions=--enable_type_checks --enable_asserts
//
// Dart test program testing type checks.
class TypeTest {
static test() {
int result = 0;
try {
int i = "hello"; // Throws a TypeError if type checks are enabled.
} catch (TypeError error) {
result = 1;
Expect.equals("int", error.dstType);
Expect.equals("String", error.srcType);
/*
Expect.equals("i", error.dstName);
int pos = error.url.lastIndexOf("/", error.url.length);
if (pos == -1) {
pos = error.url.lastIndexOf("\\", error.url.length);
}
String subs = error.url.substring(pos + 1, error.url.length);
Expect.equals("TypeTest.dart", subs);
Expect.equals(12, error.line);
Expect.equals(15, error.column);
*/
}
return result;
}
static testSideEffect() {
int result = 0;
int index() {
result++;
return 0;
}
try {
List<int> a = new List<int>(1);
a[0] = 0;
a[index()]++; // Type check succeeds, but does not create side effects.
assert(a[0] == 1);
} catch (TypeError error) {
result = 100;
}
return result;
}
static testArgument() {
int result = 0;
int f(int i) {
return i;
}
try {
int i = f("hello"); // Throws a TypeError if type checks are enabled.
} catch (TypeError error) {
result = 1;
Expect.equals("int", error.dstType);
Expect.equals("String", error.srcType);
/*
Expect.equals("i", error.dstName);
int pos = error.url.lastIndexOf("/", error.url.length);
if (pos == -1) {
pos = error.url.lastIndexOf("\\", error.url.length);
}
String subs = error.url.substring(pos + 1, error.url.length);
Expect.equals("TypeTest.dart", subs);
Expect.equals(49, error.line);
Expect.equals(15, error.column);
*/
}
return result;
}
static testReturn() {
int result = 0;
int f(String s) {
return s;
}
try {
int i = f("hello"); // Throws a TypeError if type checks are enabled.
} catch (TypeError error) {
result = 1;
Expect.equals("int", error.dstType);
Expect.equals("String", error.srcType);
/*
Expect.equals("function result", error.dstName);
int pos = error.url.lastIndexOf("/", error.url.length);
if (pos == -1) {
pos = error.url.lastIndexOf("\\", error.url.length);
}
String subs = error.url.substring(pos + 1, error.url.length);
Expect.equals("TypeTest.dart", subs);
Expect.equals(74, error.line);
Expect.equals(14, error.column);
*/
}
return result;
}
static int field;
static testField() {
int result = 0;
try {
field = "hello"; // Throws a TypeError if type checks are enabled.
} catch (TypeError error) {
result = 1;
Expect.equals("int", error.dstType);
Expect.equals("String", error.srcType);
/*
Expect.equals("field", error.dstName);
int pos = error.url.lastIndexOf("/", error.url.length);
if (pos == -1) {
pos = error.url.lastIndexOf("\\", error.url.length);
}
String subs = error.url.substring(pos + 1, error.url.length);
Expect.equals("TypeTest.dart", subs);
Expect.equals(99, error.line);
Expect.equals(15, error.column);
*/
}
return result;
}
static testAnyFunction() {
int result = 0;
Function anyFunction;
f() { };
anyFunction = f; // No error.
try {
int i = f; // Throws a TypeError if type checks are enabled.
} catch (TypeError error) {
result = 1;
Expect.equals("int", error.dstType);
/*
Expect.equals("() => var", error.srcType); TODO(regis): => Dynamic.
Expect.equals("i", error.dstName);
int pos = error.url.lastIndexOf("/", error.url.length);
if (pos == -1) {
pos = error.url.lastIndexOf("\\", error.url.length);
}
String subs = error.url.substring(pos + 1, error.url.length);
Expect.equals("TypeTest.dart", subs);
Expect.equals(123, error.line);
Expect.equals(15, error.column);
*/
}
return result;
}
static testVoidFunction() {
int result = 0;
Function anyFunction;
void acceptVoidFunObj(void voidFunObj(Object obj)) { };
void acceptObjFunObj(Object objFunObj(Object obj)) { };
void voidFunObj(Object obj) { };
Object objFunObj(Object obj) { return obj; };
anyFunction = voidFunObj; // No error.
anyFunction = objFunObj; // No error.
acceptVoidFunObj(voidFunObj);
acceptVoidFunObj(objFunObj);
acceptObjFunObj(objFunObj);
try {
acceptObjFunObj(voidFunObj); // Throws a TypeError.
} catch (TypeError error) {
result = 1;
/*
Expect.equals("(Object) => Object", error.dstType);
Expect.equals("(Object) => void", error.srcType);
Expect.equals("objFunObj", error.dstName);
int pos = error.url.lastIndexOf("/", error.url.length);
if (pos == -1) {
pos = error.url.lastIndexOf("\\", error.url.length);
}
String subs = error.url.substring(pos + 1, error.url.length);
Expect.equals("TypeTest.dart", subs);
Expect.equals(145, error.line);
Expect.equals(33, error.column);
*/
}
return result;
}
static testFunctionNum() {
int result = 0;
Function anyFunction;
void acceptFunNum(void funNum(num num)) { };
void funObj(Object obj) { };
void funNum(num num) { };
void funInt(int i) { };
void funString(String s) { };
anyFunction = funObj; // No error.
anyFunction = funNum; // No error.
anyFunction = funInt; // No error.
anyFunction = funString; // No error.
acceptFunNum(funObj); // No error.
acceptFunNum(funNum); // No error.
acceptFunNum(funInt); // No error.
try {
acceptFunNum(funString); // Throws an error.
} catch (TypeError error) {
result = 1;
/*
Expect.equals("(num) => void", error.dstType);
Expect.equals("(String) => void", error.srcType);
Expect.equals("funNum", error.dstName);
int pos = error.url.lastIndexOf("/", error.url.length);
if (pos == -1) {
pos = error.url.lastIndexOf("\\", error.url.length);
}
String subs = error.url.substring(pos + 1, error.url.length);
Expect.equals("TypeTest.dart", subs);
Expect.equals(175, error.line);
Expect.equals(28, error.column);
*/
}
return result;
}
static testBoolCheck() {
int result = 0;
try {
bool i = !"hello"; // Throws a TypeError if type checks are enabled.
} catch (TypeError error) {
result++;
Expect.equals("bool", error.dstType);
Expect.equals("String", error.srcType);
/*
Expect.equals("boolean expression", error.dstName);
int pos = error.url.lastIndexOf("/", error.url.length);
if (pos == -1) {
pos = error.url.lastIndexOf("\\", error.url.length);
}
String subs = error.url.substring(pos + 1, error.url.length);
Expect.equals("TypeTest.dart", subs);
Expect.equals(209, error.line);
Expect.equals(17, error.column);
*/
}
try {
while ("hello") {}; // Throws a TypeError if type checks are enabled.
} catch (TypeError error) {
result++;
Expect.equals("bool", error.dstType);
Expect.equals("String", error.srcType);
/*
Expect.equals("boolean expression", error.dstName);
int pos = error.url.lastIndexOf("/", error.url.length);
if (pos == -1) {
pos = error.url.lastIndexOf("\\", error.url.length);
}
String subs = error.url.substring(pos + 1, error.url.length);
Expect.equals("TypeTest.dart", subs);
Expect.equals(225, error.line);
Expect.equals(14, error.column);
*/
}
try {
do {} while ("hello"); // Throws a TypeError if type checks are enabled.
} catch (TypeError error) {
result++;
Expect.equals("bool", error.dstType);
Expect.equals("String", error.srcType);
/*
Expect.equals("boolean expression", error.dstName);
int pos = error.url.lastIndexOf("/", error.url.length);
if (pos == -1) {
pos = error.url.lastIndexOf("\\", error.url.length);
}
String subs = error.url.substring(pos + 1, error.url.length);
Expect.equals("TypeTest.dart", subs);
Expect.equals(241, error.line);
Expect.equals(20, error.column);
*/
}
try {
for (;"hello";) {}; // Throws a TypeError if type checks are enabled.
} catch (TypeError error) {
result++;
Expect.equals("bool", error.dstType);
Expect.equals("String", error.srcType);
/*
Expect.equals("boolean expression", error.dstName);
int pos = error.url.lastIndexOf("/", error.url.length);
if (pos == -1) {
pos = error.url.lastIndexOf("\\", error.url.length);
}
String subs = error.url.substring(pos + 1, error.url.length);
Expect.equals("TypeTest.dart", subs);
Expect.equals(257, error.line);
Expect.equals(13, error.column);
*/
}
try {
int i = "hello" ? 1 : 0; // Throws a TypeError if type checks are enabled.
} catch (TypeError error) {
result++;
Expect.equals("bool", error.dstType);
Expect.equals("String", error.srcType);
/*
Expect.equals("boolean expression", error.dstName);
int pos = error.url.lastIndexOf("/", error.url.length);
if (pos == -1) {
pos = error.url.lastIndexOf("\\", error.url.length);
}
String subs = error.url.substring(pos + 1, error.url.length);
Expect.equals("TypeTest.dart", subs);
Expect.equals(273, error.line);
Expect.equals(15, error.column);
*/
}
try {
if ("hello") {}; // Throws a TypeError if type checks are enabled.
} catch (TypeError error) {
result++;
Expect.equals("bool", error.dstType);
Expect.equals("String", error.srcType);
/*
Expect.equals("boolean expression", error.dstName);
int pos = error.url.lastIndexOf("/", error.url.length);
if (pos == -1) {
pos = error.url.lastIndexOf("\\", error.url.length);
}
String subs = error.url.substring(pos + 1, error.url.length);
Expect.equals("TypeTest.dart", subs);
Expect.equals(289, error.line);
Expect.equals(11, error.column);
*/
}
try {
if ("hello" || false) {}; // Throws a TypeError if type checks are enabled.
} catch (TypeError error) {
result++;
Expect.equals("bool", error.dstType);
Expect.equals("String", error.srcType);
/*
Expect.equals("boolean expression", error.dstName);
int pos = error.url.lastIndexOf("/", error.url.length);
if (pos == -1) {
pos = error.url.lastIndexOf("\\", error.url.length);
}
String subs = error.url.substring(pos + 1, error.url.length);
Expect.equals("TypeTest.dart", subs);
Expect.equals(305, error.line);
Expect.equals(11, error.column);
*/
}
try {
if (false || "hello") {}; // Throws a TypeError if type checks are enabled.
} catch (TypeError error) {
result++;
Expect.equals("bool", error.dstType);
Expect.equals("String", error.srcType);
/*
Expect.equals("boolean expression", error.dstName);
int pos = error.url.lastIndexOf("/", error.url.length);
if (pos == -1) {
pos = error.url.lastIndexOf("\\", error.url.length);
}
String subs = error.url.substring(pos + 1, error.url.length);
Expect.equals("TypeTest.dart", subs);
Expect.equals(321, error.line);
Expect.equals(20, error.column);
*/
}
try {
if (null) {}; // Throws a TypeError if type checks are enabled.
} catch (TypeError error) {
result++;
Expect.equals("bool", error.dstType);
Expect.equals("Null", error.srcType);
/*
Expect.equals("boolean expression", error.dstName);
int pos = error.url.lastIndexOf("/", error.url.length);
if (pos == -1) {
pos = error.url.lastIndexOf("\\", error.url.length);
}
String subs = error.url.substring(pos + 1, error.url.length);
Expect.equals("TypeTest.dart", subs);
Expect.equals(337, error.line);
Expect.equals(11, error.column);
*/
}
return result;
}
static int testFactory() {
int result = 0;
try {
var x = new C();
} catch (TypeError error) {
result++;
Expect.equals("C", error.dstType);
Expect.equals("Smi", error.srcType);
/*
Expect.equals("function result", error.dstName);
int pos = error.url.lastIndexOf("/", error.url.length);
if (pos == -1) {
pos = error.url.lastIndexOf("\\", error.url.length);
}
String subs = error.url.substring(pos + 1, error.url.length);
Expect.equals("TypeTest.dart", subs);
Expect.equals(472, error.line);
Expect.equals(12, error.column);
*/
}
return result;
}
static int testListAssigment() {
int result = 0;
{
var a = new List(5);
List a0 = a;
List<Object> ao = a;
List<int> ai = a;
List<num> an = a;
List<String> as = a;
}
{
var a = new List<Object>(5);
List a0 = a;
List<Object> ao = a;
try {
List<int> ai = a;
} catch (TypeError error) {
result++;
}
try {
List<num> an = a;
} catch (TypeError error) {
result++;
}
try {
List<String> as = a;
} catch (TypeError error) {
result++;
}
}
{
var a = new List<int>(5);
List a0 = a;
List<Object> ao = a;
List<int> ai = a;
List<num> an = a;
try {
List<String> as = a;
} catch (TypeError error) {
result++;
}
}
{
var a = new List<num>(5);
List a0 = a;
List<Object> ao = a;
try {
List<int> ai = a;
} catch (TypeError error) {
result++;
}
List<num> an = a;
try {
List<String> as = a;
} catch (TypeError error) {
result++;
}
}
{
var a = new List<String>(5);
List a0 = a;
List<Object> ao = a;
try {
List<int> ai = a;
} catch (TypeError error) {
result++;
}
try {
List<num> an = a;
} catch (TypeError error) {
result++;
}
List<String> as = a;
}
return result;
}
static testMain() {
Expect.equals(1, test());
Expect.equals(1, testSideEffect());
Expect.equals(1, testArgument());
Expect.equals(1, testReturn());
Expect.equals(1, testField());
Expect.equals(1, testAnyFunction());
//Expect.equals(1, testVoidFunction()); - Function type checking issue
//Expect.equals(1, testFunctionNum()); - Function type checking issue
//Expect.equals(9, testBoolCheck()); - Boolean checking on conditions is not done
//Expect.equals(1, testFactory()); - Not doing a test on factories
Expect.equals(8, testListAssigment());
}
}
class C {
factory C() {
return 1; // Implicit result type is 'C', not int.
}
}
main() {
TypeTest.testMain();
}